/*
 *	KRoC user process
 *	Copyright (C) 1995, 1996, 1997  D.C. Wood, P.H. Welch, D.J. Beckett
 *	Modification for OOS (C) 2002 Brian Vinter / Fred Barnes
 *	Modifications Copyright (C) 2000-2004 Fred Barnes
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program 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 General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * This is the UNIX process that runs the occam code and traps its
 * termination cleanly or via signals.  The co-process handles blocking
 * on fd 0 (the keyboard) and uses a UNIX signal to deliver notification
 * of a new keypress.
 *
 * $Source: /proj/ofa/rts/RCS/userproc.c,v $
 *
 * $Id: userproc.c,v 1.11 1997/08/12 14:38:28 djb1 Exp $
 *
 * (C) Copyright 1995,1996,1997
 *    D.C. Wood <D.C.Wood@ukc.ac.uk>
 *    P.H. Welch <P.H.Welch@ukc.ac.uk>
 *    Dave Beckett <D.J.Beckett@ukc.ac.uk>
 * University of Kent at Canterbury
 *
 * Modifications Copyright (C) 2000-2002 Fred Barnes
 */

#ifdef HAVE_CONFIG_H
	#include <config.h>
#endif

#if defined(OOS_BUILD)
	#include <oos_funcs.h>
#else	/* !OOS_BUILD */
	#include <unistd.h>
	#ifdef HAVE_STDLIB_H
		#include <stdlib.h>
	#endif
	#include <fcntl.h>
	#include <setjmp.h>
#endif	/* !OOS_BUILD */

#include <rts.h>
#include <arch/sched_types.h>

#if (defined(USEFUL_SEGFAULT) || defined(USEFUL_FPEDEBUG)) && !defined(OOS_BUILD)
	#include <signal.h>
#endif

#ifndef OOS_BUILD
	#ifdef ENABLE_CPU_TIMERS
		#include <arch/timer.h>
	#endif
#endif

#include <dmem_if.h>
#include <arch/deadlock.h>

#if !defined(OOS_BUILD)
	#ifdef BLOCKING_SYSCALLS
		#include <bsyscalls_if.h>

		static struct sigaction bsigact;
	#endif	/* BLOCKING_SYSCALLS */
#endif

#if defined(OOS_BUILD)
	#define MESSAGE(fmt,args...) oos_printk(fmt,##args)
	#define FFLUSH(stream)
#else
	#define MESSAGE(fmt,args...) fprintf (stderr,fmt,##args)
	#define FFLUSH(stream) fflush(stream)
#endif

/*{{{  global variables*/
extern sync_type sf;


/*}}}*/
/*{{{  "local" variables*/
/* These are used in sched.s */
#if !defined(OOS_BUILD)
FILE *kroc_in;         /* not used */
FILE *kroc_out;
FILE *kroc_err;

int kbd_char_signalled = 0;
sigset_t sig_mask, zero_mask;
int trace_count = 0;

char TTin_buf;
jmp_buf kroc_out_jump_buffer;
sigjmp_buf kroc_signal_jump_buffer;
#endif


/*}}}*/
/*{{{  private delcarations*/
#if !defined(OOS_BUILD)
static int kbdproc_pid, to_kbdproc, from_kbdproc;
static int stdin_is_tty;

static volatile int faulted;
#endif

/* Floating point linkup */
extern int fpe_debug (void);

#if !defined(OOS_BUILD)
static void user_stop_signal (void);
static void user_cont_signal (void);
#endif	/* !OOS_BUILD */

static void userproc_exit (int exit_status, bool dump_core);

#if !defined(OOS_BUILD)
	static void set_user_process_signals (void);

	#ifdef USEFUL_SEGFAULT
	static struct sigaction segv_action;
	static void user_segv_handler (int sig, siginfo_t *siginfo, void *sptr);
	#endif
	#ifdef USEFUL_FPEDEBUG
	static struct sigaction fpe_action;
	static void user_fpe_handler (int sig, siginfo_t *siginfo, void *sptr);
	#endif
#endif

extern void show_last_debug_insert (void);
/*}}}*/

/*{{{  void occam_exit (int exit_status, int occam_iptr)*/
/*
 *	called from run-time kernel to exit
 */
void occam_exit (int exit_status, int occam_iptr)
{
	#ifndef ALONE
		if (occam_iptr != 0) {
			MESSAGE ("OCCAM STOPped at %08x\n", occam_iptr);
		}
	#endif
	userproc_exit(exit_status, 0);
	return;
}
/*}}}*/
/*{{{  static void userproc_exit (int exit_status, bool dump_core) */
/*
 *	local exit function
 */
static void userproc_exit (int exit_status, bool dump_core) 
{
#if 0
fprintf (stderr, "KRoC: userproc_exit (%d, %d)\n", exit_status, dump_core);
#endif
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: Exit %d\n", exit_status);
			FFLUSH (stderr);
		}
	#endif

	#ifdef DEBUG_RTS
	if (trace) {
		MESSAGE ("USERPROC: About to kill KBDPROC\n");
		FFLUSH (stderr);
	}
	#endif

	#if defined(OOS_BUILD)
		/* shutdown stuff ?? */
	#else
		#if defined(BLOCKING_SYSCALLS)
			/* Kill any clones */
			bsyscalls_destroy_clones ();
		#else	/* !defined(BLOCKING_SYSCALLS) */
			/* Terminate keyboard process, if still around */
			kill (kbdproc_pid, SIGTERM);
		#endif	/* !defined(BLOCKING_SYSCALLS) */
	#endif

	/* reset terminal */
	#if !defined(OOS_BUILD)
		if (stdin_is_tty) {
			restore_tty_state ();
		}
	#endif	/* !OOS_BUILD */
	show_last_debug_insert ();
	if (do_print_memstats) {
		/* dump dynamic memory stuff */
		dmem_usagedump ();
	}
	#ifdef DM_DEBUG
	dm_debug_dump ();
	#endif
	dmem_shutdown ();

	#if defined(GENERATE_CORES)
		if (dump_core) {
			MESSAGE ("KRoC: Fatal error code %d, core dumped\n", exit_status);
			FFLUSH (stderr);
			abort ();
		}
	#endif /* GENERATE_CORES */
#if 0
fprintf (stderr, "KRoC: userproc_exit(): exiting proper\n");
#endif

	#if defined(OOS_BUILD)
	oos_panic ("exit");
	#else
	_exit (exit_status);
	#endif
}
/*}}}*/
/*{{{  void user_bad_exit (void)*/
/*
 *	bad exit (causes core-dump)
 */
void user_bad_exit (void)
{
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("KRoC: userproc: I've been murdered!\n");
		}
	#endif
	userproc_exit (-8, 1);
}
/*}}}*/
/*{{{  void user_good_exit (void)*/
/*
 *	good exit (status 0)
 */
void user_good_exit (void)
{
	userproc_exit (0, 0);
}
/*}}}*/
#if !defined(OOS_BUILD)
/*{{{  static void user_signal_good_exit (void)*/
/*
 *	good exit from signal handler
 */
static void user_signal_good_exit (void)
{
	siglongjmp (kroc_signal_jump_buffer, 1);
}
/*}}}*/
/*{{{  static void user_signal_quit_exit (int sig)*/
/*
 *	sigquit exit (status 0, but with deadlock stuff)
 */
static void user_signal_quit_exit (int sig)
{
	siglongjmp (kroc_signal_jump_buffer, 3);
}
/*}}}*/
#endif

/* SIGNAL handlers */

#ifndef OOS_BUILD
/*{{{  void user_tim_handler (void)*/
/*
 *	timer signal (SIGALRM) handler
 *	SIGALRM  handler - only set during hibernate
 */
void user_tim_handler (void)
{
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: Alarm ringing (SIGALRM).\n");
			FFLUSH (stderr);
		}
	#endif
	sf.u.s.tim_sync = 1;
	#if defined(SIGNAL_TYPE_SYSV)
		signal (SIGALRM, (SigParam)user_tim_handler);
	#endif /* SIGNAL_TYPE */
}
/*}}}*/

#ifdef BLOCKING_SYSCALLS
/*{{{  void user_blocking_handler (void)*/
/*
 *	blocking syscall handler (when a call finishes) -- SIGUSR1
 */
void user_blocking_handler (void)
{
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: Blocking system call finished (SIGBLOCKING = (SIGUSR1))\n");
		}
	#endif
	sf.u.s.block_sync = 1;
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: Set block_sync\n");
		}
	#endif
}
/*}}}*/
#else	/* !BLOCKING_SYSCALLS */
/*{{{  void user_kbd_handler (void)*/
/*
 *	keyboard signal (SIGKBD) handler (generated from kbdproc.c)
 */
void user_kbd_handler (void)
{
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: Character #%d arriving.\n", trace_count);
			++trace_count;
		}
	#endif
	kbd_char_signalled = 1;
	sf.u.s.kbd_sync = 1;
	#if defined(SIGNAL_TYPE_SYSV)
		signal (SIGKBD, (SigParam)user_kbd_handler);
	#endif /* SIGNAL_TYPE */
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: set keyboard sync.");
			++trace_count;
		}
	#endif
}
/*}}}*/
#endif	/* !BLOCKING_SYSCALLS */

/*{{{  void user_overflow (void)*/
/*
 *	probably redundant..
 */
void user_overflow (void)
{
	userproc_exit (1, 0);
}
/*}}}*/

#ifdef USEFUL_SEGFAULT
/*{{{  static void user_segv_handler (int sig, siginfo_t *siginfo, void *sptr)*/
/*
 *	verbose SIGSEGV handler
 */
static void user_segv_handler (int sig, siginfo_t *siginfo, void *sptr)
{
	if (siginfo->si_signo != SIGSEGV) {
		MESSAGE ("KRoC: in user_segv_handler (), but not segv ?\n");
	} else {
		MESSAGE ("KRoC: segmentation fault:\n");
		MESSAGE ("    errno = %d\n", siginfo->si_errno);
		MESSAGE ("    origin = %d (", siginfo->si_code);
		switch (siginfo->si_code) {
		case SI_USER:
			MESSAGE ("user)\n");
			break;
		case SI_QUEUE:
			MESSAGE ("queue)\n");
			break;
		case SI_TIMER:
			MESSAGE ("timer)\n");
			break;
		case SI_MESGQ:
			MESSAGE ("mesgq)\n");
			break;
		case SI_ASYNCIO:
			MESSAGE ("asyncio)\n");
			break;
		case SI_SIGIO:
			MESSAGE ("sigio)\n");
			break;
		default:
			if (siginfo->si_code > 0) {
				MESSAGE ("kernel)\n");
			} else {
				MESSAGE ("[dunno])\n");
			}
			break;
		}
		MESSAGE ("    fault at 0x%x\n", (unsigned int)siginfo->si_addr);
		MESSAGE ("    sptr at  0x%x\n", (unsigned int)sptr);
	}
	if (!faulted) {
		faulted = 1;
		userproc_exit (1, 1);
	} else {
		kill (getpid (), SIGKILL);
	}
	return;
}
/*}}}*/
#endif	/* USEFUL_SEGFAULT */
#ifdef USEFUL_FPEDEBUG
/*{{{  static void user_fpe_handler (int sig, siginfo_t *siginfo, void *sptr)*/
/*
 *	verbose SIGFPE handler
 */
static void user_fpe_handler (int sig, siginfo_t *siginfo, void *sptr)
{
	if (siginfo->si_signo != SIGFPE) {
		MESSAGE ("KRoC: in user_fpe_handler (), but not FPE ?\n");
	} else {
		MESSAGE ("KRoC: floating point exception:\n");
		switch (siginfo->si_code) {
		case FPE_INTDIV:
			MESSAGE ("    integer division by zero\n");
			break;
		case FPE_INTOVF:
			MESSAGE ("    integer overflow\n");
			break;
		case FPE_FLTDIV:
			MESSAGE ("    floating point divide by zero\n");
			break;
		case FPE_FLTOVF:
			MESSAGE ("    floating point overflow\n");
			break;
		case FPE_FLTUND:
			MESSAGE ("    floating point underflow\n");
			break;
		case FPE_FLTRES:
			MESSAGE ("    inexact result\n");
			break;
		case FPE_FLTINV:
			MESSAGE ("    invalid operation\n");
			break;
		case FPE_FLTSUB:
			MESSAGE ("    subscript out of range\n");
			break;
		default:
			MESSAGE ("    unspecified\n");
			break;
		}
		MESSAGE ("    fault at 0x%x\n", (unsigned int)siginfo->si_addr);
		MESSAGE ("    sptr at  0x%x\n", (unsigned int)sptr);
	}
	userproc_exit (1, 0);
	return;
}
/*}}}*/
#endif	/* USEFUL_FPEDEBUG */


#if defined(SIGNAL_TYPE_SYSV)
/*{{{  void user_trap_handler (int sig)*/
void user_trap_handler (int sig)
{
	if (sig == SIGSEGV) {
		MESSAGE ("KRoC: Segmentation fault.\n");
	} else {
		MESSAGE ("KRoC: Range error / STOP executed (signal %d)\n", sig);
	}
	if (!faulted) {
		faulted = 1;
		userproc_exit (1, 1);
	} else {
		/* Faulted during fault.. */
		kill (getpid(), SIGKILL);
	}
} /* user_trap_handler */
/*}}}*/
/*{{{  void user_fp_handler (int sig)*/
void user_fp_handler (int sig)
{
	if (fpe_debug ()) {
		MESSAGE ("KRoC: Floating-point error\n");
		userproc_exit (42, 1);
	} else {
		userproc_exit (41, 0);
	}
}
/*}}}*/
#else
/*{{{  void user_trap_handler (int sig, int code, struct sigcontext *scp, char *addr)*/
void user_trap_handler (int sig, int code, struct sigcontext *scp, char *addr)
{
	/*      fprintf (stderr, "sig = 0x%x, code = 0x%x\n", sig, code);       */
	MESSAGE ("KRoC: ");
	switch (code & 0x1f) {           /* why? */
	case _SETERR:
		MESSAGE ("Set error");
		break;
	case _OFLOW:
		MESSAGE ("Integer overflow");
		break;
	case _RANGE:
		MESSAGE ("Range error");
		break;
	case _FLOAT:
		MESSAGE ("Floating-point error");
		break;
	case _UNREC:
		MESSAGE ("Unrecognized instruction");
		break;
	case _UNIMP:
		MESSAGE ("Unimplemented instruction");
		break;
	default:
		MESSAGE ("Unrecognized trap number 0x%x");
		break;
	}
	MESSAGE ("\n");
	FFLUSH (stderr);
	userproc_exit (code, 1);
} /* user_trap_handler */
/*}}}*/
/*{{{  void user_fp_handler (int sig, int code, struct sigcontext *scp, char *addr)*/
void user_fp_handler (int sig, int code, struct sigcontext *scp, char *addr)
{
	switch(code) {
	#ifdef FPE_INTOVF_TRAP
	case FPE_INTOVF_TRAP:
		MESSAGE ("KRoC: FP error - integer overflow\n");
		break;
	#endif      
	#ifdef FPE_STARTSIG_TRAP
	case FPE_STARTSIG_TRAP:
		MESSAGE ("KRoC: FP error - process using fp\n");
		break;
	#endif      
	#ifdef FPE_INTDIV_TRAP
	case FPE_INTDIV_TRAP:
		MESSAGE ("KRoC: Floating Point error - integer divide by zero\n");
		break;
	#endif      
	#ifdef FPE_FLTINEX_TRAP
	case FPE_FLTINEX_TRAP:
		MESSAGE ("KRoC: Floating Point error - floating inexact result\n");
		break;
	#endif      
	#ifdef FPE_FLTDIV_TRAP
	case FPE_FLTDIV_TRAP:
		MESSAGE ("KRoC: Floating Point error - floating divide by zero\n");
		break;
	#endif      
	#ifdef FPE_FLTUND_TRAP
	case FPE_FLTUND_TRAP:
		MESSAGE ("KRoC: Floating Point error - floating underflow\n");
		break;
	#endif      
	#ifdef FPE_FLTOPERR_TRAP
	case FPE_FLTOPERR_TRAP:
		MESSAGE ("KRoC: Floating Point error - floating operand error\n");
		break;
	#endif      
	#ifdef FPE_FLTOVF_TRAP
	case FPE_FLTOVF_TRAP:
		MESSAGE ("KRoC: Floating Point error - floating overflow\n");
		break;
	#endif      
	default:
		MESSAGE ("KRoC: Unknown floating point error number 0x%x\n", code);
		break;
	}
	userproc_exit (42, 1);
}
/*}}}*/
#endif /* SIGNAL_TYPE */


/*{{{  static void user_stop_signal (void)*/
static void user_stop_signal (void)
{
	/* Advanced Programming in the UNIX Environment, Stevens; P321 */
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: Stopped\n");
			FFLUSH(stderr);
		}
	#endif

	/* set continue handler for after stop */
	signal (SIGCONT, (SigParam)user_cont_signal);

	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: Before kill()\n");
			FFLUSH(stderr);
		}
	#endif

	if (stdin_is_tty) {
		restore_tty_state ();
		#ifdef DEBUG_RTS
			if (trace) {
				MESSAGE ("USERPROC: Restored tty state\n");
				FFLUSH(stderr);
			}
		#endif
	}

	#ifndef BLOCKING_SYSCALLS
		/* Stop the KBDPROC */
		kill (kbdproc_pid, SIGSTOP);
	#else	/* BLOCKING_SYSCALLS */
		kill_kbdio ();
	#endif	/* BLOCKING_SYSCALLS */

	/* Do the default SIGTSTP action - stop this process */
	kill (getpid(), SIGSTOP);

	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: After kill()\n");
			FFLUSH(stderr);
		}
	#endif

	#if defined(SIGNAL_TYPE_SYSV)
		/* Re-establish signal handler */
		signal (SIGTSTP, (SigParam)user_stop_signal);
	#endif
}
/*}}}*/
/*{{{  static void user_cont_signal (void)*/
static void user_cont_signal (void)
{
	/* Advanced Programming in the UNIX Environment, Stevens; P321 */
	sigset_t mask;

#if 0
fprintf (stderr, "USERPROC: user_cont_signal: ...\n");
#endif
	#ifdef DEBUG_RTS
		if (trace) {
			fprintf(stderr, "USERPROC: In user_cont_signal()\n");  FFLUSH(stderr);
		}
	#endif

	/* unblock SIGCONT since it is blocked while we're handling it */
	sigemptyset (&mask);
	sigaddset (&mask, SIGCONT);
	sigprocmask (SIG_UNBLOCK, &mask, NULL);

	if (stdin_is_tty) {
		/* Save terminal state */
		#ifdef DEBUG_RTS
			if (trace) {
				MESSAGE ("USERPROC: Saving tty state\n");
				FFLUSH(stderr);
			}
		#endif
		save_tty_state ();
	}

	/* Do default SIGCONT action - continue the process */
	signal (SIGCONT, SIG_DFL);
	kill (getpid(), SIGCONT);
}
/*}}}*/
#endif	/* !OOS_BUILD */


#if !defined(BLOCKING_SYSCALLS) && !defined(OOS_BUILD)
/*{{{  char read_the_kbd (void)*/
/* called from assembler */
char read_the_kbd (void)
{
	if (read (from_kbdproc, &TTin_buf, 1) != 1) {
		MESSAGE ("USERPROC: Can't read tty char from KBDPROC\n");
		FFLUSH (stderr);
		userproc_exit (3, 0);
	}
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: '%c' (0x%x) was read from KBDPROC\n", Tin_buf, TTin_buf);
			FFLUSH (stderr);
		}
	#endif
	return (TTin_buf);            /* dcw */
} /* read_the_kbd */
/*}}}*/
/*{{{  void acknowledge (void)*/
/* called from assembler */
void acknowledge (void)
{       
	sf.u.s.kbd_sync = 0;
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: Acknowledge KBDPROC\n");
			FFLUSH (stderr);
		}
	#endif

	if (write (to_kbdproc, &TTin_buf, 1) != 1) {
		#ifdef DEBUG_RTS
			if (trace) {
				MESSAGE ("USERPROC: Can't (pipe) acknowledge keyboard\n");
				userproc_exit (3, 0);
			}
		#endif
	}
}  /* acknowledge */
/*}}}*/
/*{{{  void user_to_kbd_sync (void)*/
void user_to_kbd_sync (void)
{
	char go_char;

	/* give go-ahead to keyboard process */
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: Signalling to KBDPROC\n");
			FFLUSH (stderr);
		}
	#endif
	if (write (to_kbdproc, "c", 1) != 1) {
		MESSAGE ("USERPROC: Can't write go-ahead\n");
		FFLUSH (stderr);
		userproc_exit (3, 0);
	}

	/* wait for keyboard process's go-ahead */
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: Waiting for KBDPROC\n");
			FFLUSH (stderr);
		}
	#endif

	if ((read (from_kbdproc, &go_char, 1) != 1) || (go_char != 'p')) {
		MESSAGE ("USERPROC: can't read KBDPROC's go-ahead '%c'\n", go_char);
		userproc_exit (3, 0);
	}
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: Got KBDPROC go-ahead\n");
			FFLUSH (stderr);
		}
	#endif
} /* user_to_kbd_sync */
/*}}}*/
#endif	/* !BLOCKING_SYSCALLS */

#if !defined(OOS_BUILD)
/*{{{  static void set_user_process_signals (void)*/
/*
 *	sets up signal handling for KRoC
 */
static void set_user_process_signals (void)
{
	/* Signals used by KRoC kernel */

	#ifdef BLOCKING_SYSCALLS
		/* Modified kernel block_sync variable */
		bsigact.sa_handler = (SigParam)user_blocking_handler;
		bsigact.sa_flags = SA_RESTART;
		sigemptyset (&bsigact.sa_mask);
		if (sigaction (SIGBLOCKING, &bsigact, NULL)) {
			MESSAGE ("KRoC: unable to set handler for signal SIGBLOCKING (%d)\n", SIGBLOCKING);
			userproc_exit (1, 0);
		}
	#else	/* !BLOCKING_SYSCALLS */
		/* Modifies kernel kbd_sync variable */
		signal (SIGKBD, (SigParam)user_kbd_handler);
	#endif	/* !BLOCKING_SYSCALLS */
	/* Modifies kernel tim_sync variable */
	signal (SIGALRM, (SigParam)user_tim_handler);
	#if 0
		/* Called by user if they panic */
		signal (SIGPANIC, (SigParam)user_panic_handler);
	#endif

	/* Process termination signals */
	signal (SIGHUP, (SigParam)user_signal_good_exit);		/* hangup */
	#if 0
		signal (SIGEMT, (SigParam)user_bad_exit);	/* emulator trap */
	#endif
	signal (SIGTERM, (SigParam)user_signal_good_exit);		/* software termination */
	signal (SIGPIPE, (SigParam)user_signal_good_exit);		/* broken pipe -- usually from "prog | head -10" or similar */

	/* Only catch if not being ignored */
	if (signal (SIGINT, SIG_IGN) != SIG_IGN) {
		signal (SIGINT, (SigParam)user_signal_good_exit);	/* interrupt (^C) */
	}
	if (signal (SIGQUIT, SIG_IGN) != SIG_IGN) {
		signal (SIGQUIT, (SigParam)user_signal_quit_exit);	/* quit */
	}

	#ifdef BLOCKING_SYSCALLS
		/* can safely ignore child processes exiting.. */
		signal (SIGCHLD, SIG_IGN);
	#endif

	/* 'Error' signals */
	signal (SIGILL, (SigParam)user_trap_handler);  /* illegal instruction */
	signal (SIGBUS, (SigParam)user_trap_handler);  /* bus error */
	faulted = 0;
	#ifdef USEFUL_SEGFAULT
		segv_action.sa_sigaction = user_segv_handler;
		segv_action.sa_flags = SA_SIGINFO;
		sigemptyset (&segv_action.sa_mask);
		if (sigaction (SIGSEGV, &segv_action, NULL) < 0) {
			MESSAGE ("KRoC: unable to sigaction SEGV, using normal.\n");
			signal (SIGSEGV, (SigParam)user_trap_handler);
		}
	#else	/* !USEFUL_SEGFAULT */
		#ifndef DEFAULT_CORES
			signal (SIGSEGV, (SigParam)user_trap_handler); /* segmentation violation */
		#endif
	#endif	/* !USEFUL_SEGFAULT */
	#if defined(TARGET_CPU_ALPHA)
		signal (SIGTRAP, (SigParam)user_trap_handler);
	#endif
	#ifdef USEFUL_FPEDEBUG
		fpe_action.sa_sigaction = user_fpe_handler;
		fpe_action.sa_flags = SA_SIGINFO;
		sigemptyset (&fpe_action.sa_mask);
		if (sigaction (SIGFPE, &fpe_action, NULL) < 0) {
			MESSAGE ("KRoC: unable to sigaction FPE, using normal.\n");
			signal (SIGFPE, (SigParam)user_fp_handler);
		}
	#else	/* !USEFUL_FPEDEBUG */
		signal (SIGFPE, (SigParam)user_fp_handler);
	#endif	/* !USEFUL_FPEDEBUG */

	sigemptyset (&zero_mask);
	sigemptyset (&sig_mask);
	#if defined(BLOCKING_SYSCALLS)
		sigaddset (&sig_mask, SIGBLOCKING);
	#else
		sigaddset (&sig_mask, SIGKBD);
	#endif
	sigaddset (&sig_mask, SIGALRM);

	/* stop signal generated from keyboard (^Z) */
	/* Only catch this if running with job-control */
	if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) {
		signal (SIGTSTP, (SigParam)user_stop_signal);
	}
}
/*}}}*/
#endif	/* !OOS_BUILD */

/*{{{  void impossible (int n, int occam_iptr)*/
/* (probably not) called from scheduler */
void impossible (int n, int occam_iptr)
{       
	MESSAGE ("\nKRoC: *** Impossible thing number %d happened at occam address %08X***\n", n, occam_iptr);
	MESSAGE ("KRoC: Please report thing number with your bug report\n");
	userproc_exit (4, 0);
} /* impossible */
/*}}}*/
/*{{{  void status (int erfl)*/
/* state of error flag */
void status (int erfl)
{       
	if (erfl) {
		MESSAGE ("KRoC: Error flag set\n");
		FFLUSH (stderr);
	}
} /* status */
/*}}}*/
/*{{{  void dead (int erfl)*/
/* called by scheduler */
void dead (int erfl)
{       
	MESSAGE ("\nKRoC: Program deadlocked (no processes to run)\n");
	FFLUSH (stderr);              /* still in raw mode */
	status (erfl);
	userproc_exit (2, 0);
} /* dead */
/*}}}*/
/*{{{  void dead_quiet (int erfl)*/
void dead_quiet (int erfl)
{
	FFLUSH (stderr);
	status (erfl);
	userproc_exit (2, 0);
} /* dead_quiet */
/*}}}*/
/*{{{  unsigned int utime (void)*/
/* user (processor) time */
unsigned int utime (void)
{
	#if defined(HAVE_GETRUSAGE)
		struct rusage usage;          /* Resolution is 10000 usec */

		(void) getrusage (RUSAGE_SELF, &usage);
		return usage.ru_utime.tv_sec*1000000 + usage.ru_utime.tv_usec;
	#elif defined(HAVE_TIMES)
		#include <sys/times.h>

		struct tms buffer;

		(void)times(&buffer);  /* Counts in 1/HZ seconds (where HZ is 60, for SunOS) */
		return buffer.tms_utime * (1000000 / HZ);
	#else
		return rtime(); /* No processor time available */
	#endif
} /* utime */
/*}}}*/
/*{{{  unsigned int rtime (void)*/
/* real (clock) time    */
unsigned int rtime (void)
{
	/* resolution 1 usec? -- around that..  */
#ifdef OOS_BUILD
	struct oos_timeval tp;
#else
	struct timeval tp;
#endif

	#if defined(OOS_BUILD)
		oos_gettimeofday (&tp);
	#else
		(void) gettimeofday (&tp, 0);
	#endif
	#ifdef DEBUG_RTS
		fprintf(stderr,"rtime: time returning is %d\n",tp.tv_sec*1000000 + tp.tv_usec);
	#endif
	return (tp.tv_sec*1000000) + tp.tv_usec;
} /* rtime */
/*}}}*/


/*
  This fixed constant of 10ms is added to each setitimer call to
  kludge (get round) a Solaris 2.3 (or later?) bug in returning too early.
  */
#if defined(SOLARIS_TIMER_BUG) && !defined(OOS_BUILD)
	#include <sys/param.h>
	static int quantum; 
#endif


#if !defined(HAVE_UALARM) && !defined(OOS_BUILD)
	#include <sys/time.h>

/*{{{  int ualarm(int delay, int interval)*/
int ualarm(int delay, int interval)
{
	struct itimerval itv;
	#ifdef DEBUG_RTS
		MESSAGE ("ualarm: delay = %d, interval = %d\n", delay, interval);
	#endif
	#ifdef SOLARIS_TIMER_BUG
		delay += quantum; 
	#endif
	itv.it_value.tv_sec =delay / 1000000;
	itv.it_value.tv_usec = delay % 1000000;

	itv.it_interval.tv_sec=0;
	itv.it_interval.tv_usec=interval;
	return setitimer(ITIMER_REAL, &itv, (struct itimerval*)NULL);
}
/*}}}*/
#endif /* HAVE_UALARM */

/*{{{  int safe_pause (void)*/
int safe_pause (void)
/*
 *	Called from run-time kernel
 */
{       
	#if !defined(OOS_BUILD)
	sigset_t old_mask;
	#endif
	#ifdef DEBUG_RTS
		fprintf(stderr, "USERPROC: safepause() entered\n");
	#endif
	#if !defined(OOS_BUILD)
	if (sigprocmask (SIG_BLOCK, &sig_mask, &old_mask) < 0) { /* critical region */
		MESSAGE ("USERPROC: sigprocmask (SIG_BLOCK, ... , ...) failed.\n");
		userproc_exit (3, 0);
	}
	#endif

	#if defined(TARGET_CPU_ALPHA)
		while ((sf.u.s.kbd_sync & 1) == 0 || (sf.u.s.tim_sync & 1) == 0)
	#else
		while (sf.u.combined_sync == 0)  /* all in one word now */
	#endif
	{ /* signal hasn't happened yet */
		#if !defined(OOS_BUILD)
		if (sigsuspend (&zero_mask) != -1) {                /* wait for it */
			MESSAGE ("USERPROC: sigsuspend (...) failed.\n");
			userproc_exit (3, 0);
		}
		#endif

		#if defined(TARGET_CPU_ALPHA)
			if ((sf.u.s.kbd_sync & 1) == 1 || (sf.u.s.tim_sync & 1) == 1) {
				break;
			}
		#else
			if (sf.u.combined_sync != 0) {
				/* all in one word now */
				break;
			}
		#endif
	} /* while */


	#if !defined(OOS_BUILD)
	if (sigprocmask (SIG_SETMASK, &old_mask, NULL) < 0) {   /* end of critical */
		MESSAGE ("USERPROC: sigprocmask (SIG_SETMASK, ..., ...) failed.\n");
		userproc_exit (3, 0);
	}
	#endif
	#ifdef DEBUG_RTS
		fprintf(stderr, "USERPROC: safepause about to exit (return 0)\n"); /* Jim */
	#endif
	return 0;
} /* safe_pause */
/*}}}*/

#if defined(ENABLE_CPU_TIMERS) && !defined(OOS_BUILD)
/*{{{  int safe_pause_timeout (int usecs)*/
int safe_pause_timeout (int usecs)
/*
 *	Called from run-time kernel (this one has a timeout)
 */
{       
	sigset_t old_mask;

	#ifdef DEBUG_RTS
		fprintf(stderr, "USERPROC: safepause() entered\n");
	#endif
	/* this can happen as it turns out .. */
	if (usecs <= 0) {
		return 0;
	}


	if (sigprocmask (SIG_BLOCK, &sig_mask, &old_mask) < 0) { /* critical region */
		MESSAGE ("USERPROC: sigprocmask (SIG_BLOCK, ... , ...) failed.\n");
		userproc_exit (3, 0);
	}
#if 0
fprintf (stderr, "KRoC: safe_pause_timeout(): setting alarm for %d usecs\n", usecs);
#endif
	Time_SetAlarm (usecs);
	#if defined(TARGET_CPU_ALPHA)
		while ((sf.u.s.kbd_sync & 1) == 0 || (sf.u.s.tim_sync & 1) == 0)
	#else
		while (sf.u.combined_sync == 0)  /* all in one word now */
	#endif
	{ /* signal hasn't happened yet */
		if (sigsuspend (&zero_mask) != -1) {                /* wait for it */
			MESSAGE ("USERPROC: sigsuspend (...) failed.\n");
			userproc_exit (3, 0);
		}

	#if defined(TARGET_CPU_ALPHA)
		if ((sf.u.s.kbd_sync & 1) == 1 || (sf.u.s.tim_sync & 1) == 1)
	#else
		if (sf.u.combined_sync != 0)  /* all in one word now */
	#endif
			break;
	} /* while */
	if (!(sf.u.s.tim_sync & 1)) {
		Time_ResetAlarm ();
	}
	sf.u.s.tim_sync = 0;
	if (sigprocmask (SIG_SETMASK, &old_mask, NULL) < 0) {   /* end of critical */
		MESSAGE ("USERPROC: sigprocmask (SIG_SETMASK, ..., ...) failed.\n");
		userproc_exit (3, 0);
	}
	#ifdef DEBUG_RTS
		fprintf(stderr, "USERPROC: safepause about to exit (return 0)\n"); /* Jim */
	#endif
	return 0;
}
/*}}}*/
#endif	/* ENABLE_CPU_TIMERS */


/* user-process code below here */

/*{{{  _occ_enter/_occ_exit declarations*/
extern void _occ_enter (void);
extern int _occ_exit (void);

/*}}}*/
/*{{{  void user_process (int kbd_pid, int to_kbd_fd, int from_kbd_fd, bool is_a_tty)*/
void user_process (int kbd_pid, int to_kbd_fd, int from_kbd_fd, bool is_a_tty)
{
	static int erfl;
	static int sigjmpcode;

	#if !defined(OOS_BUILD)
		/* kroc channels */
		kroc_in = stdin;
		kroc_out = stdout;
		kroc_err = stderr;

		/* user_process - run occam */
		kbdproc_pid = kbd_pid;
		to_kbdproc = to_kbd_fd;
		from_kbdproc = from_kbd_fd;
		stdin_is_tty = is_a_tty;
	#endif

	#ifdef SOLARIS_TIMER_BUG
		quantum=1000000/HZ;
	#endif

	#if !defined(OOS_BUILD)
		sigjmpcode = sigsetjmp (kroc_signal_jump_buffer, 0);
		if (sigjmpcode) {
			goto signalled;
		}
		set_user_process_signals ();
		kbd_char_signalled = 0;
	#endif
	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: About to synchronise with KBDPROC\n");
			FFLUSH (stderr);
		}
	#endif

	#if !defined(BLOCKING_SYSCALLS) && !defined(OOS_BUILD)
		user_to_kbd_sync ();
	#endif	/* !BLOCKING_SYSCALLS */

	#ifdef DEBUG_RTS
		if (trace) {
			MESSAGE ("USERPROC: Synchronised ok\n");
			FFLUSH (stderr);
		}
	#endif

	#if defined(OOS_BUILD)
		_occ_enter ();
	#else
		/* really do the occam thing */
		if (!setjmp (kroc_out_jump_buffer)) {
			/* Jim Added underscore to fix assembler clashing with 386 enter instruction */
			#ifdef DEBUG_KERNEL
				fprintf( stderr, "USERPROC: about to call _occ_enter() (%p)\n", _occ_enter );
			#endif
			FFLUSH (stderr);
			_occ_enter ();			/* run the occam program */ 
		} else {
			erfl = _occ_exit ();		/* cleanup */
		}
	#endif
	#ifdef BLOCKING_DEBUG
		MESSAGE ("KRoC: OUT OF _occ_enter!\n");
	#endif

	/*fflush (stdout);*/              /* clear buffer */
	status (erfl);
	userproc_exit (erfl, 0);

signalled:
	switch (sigjmpcode) {
	case 1:
		/* good exit */
		userproc_exit (0, 0);
		break;
	case 2:
		/* bad exit */
		userproc_exit (1, 0);
		break;
	default:
		/* quit exit or other */
#ifdef OOS_BUILD
		kernel_dead (1);
#else
		kernel_dead (SIGQUIT);
#endif
		userproc_exit (0, 0);
		break;
	}
} /* user_process */
/*}}}*/


