avoiding 4.2BSD restart of read() after signal

VLD/VMB gwyn at BRL.ARPA
Sat Jan 18 00:41:49 AEST 1986


/*
	signal -- old system call emulation for 4.2BSD (VAX & Gould)
		(adapted from BRL UNIX System V emulation for 4.2BSD)

	last edit:	10-Jan-1986	D A Gwyn

	NOTE:  Although this module contains CPU-specific code, it
	should be possible to adapt it to other implementations of
	4.2BSD.  The difficulty lies in avoiding the automatic restart
	of certain system calls when the signal handler returns.  I use
	here a trick first described by Donn Seeley of UCSD Chem. Dept.
*/

#include	<errno.h>
#include	<signal.h>
#include	<syscall.h>

extern int	sigvec();
extern int	sigsetmask();

#ifdef sel
extern int	csrt();			/* start of executable text */
#endif
extern		etext;			/* end of executable text */
extern int	errno;

static int	(*handler[NSIG])() =	/* "current handler" memory */
	{
	BADSIG				/* initially, unknown state */
	};
static int	inited = 0;		/* for initializing above */

static int	catchsig();


#ifdef sel
extern int	__ret_eintr();
asm("	.text");
asm("	.globl	___ret_intr");
asm("	.align	2");
asm("___ret_eintr:");
asm("	movw	#4,r0		-- EINTR");
asm("	jmp	cerror		-- acts like error return from syscall");
asm("	.data");
#else
static int
__ret_eintr()				/* substitute for system call */
{
	errno = EINTR;			/* <-- resumed starting here */
	return -1;
}
#endif


int	(*
signal( sig, func )			/* returns previous handler */
	)()
	register int	sig;		/* signal affected */
	register int	(*func)();	/* new handler */
	{
	register int	(*retval)();	/* previous handler value */
	struct sigvec	oldsv;		/* previous state */
	struct sigvec	newsv;		/* state being set */

	if ( func >= (int (*)())&etext	/* "lint" hates this */
#ifdef sel
	  || func != SIG_DFL
	  && func != SIG_IGN
	  && func <= (int (*)())csrt
#endif
	   )	{
		errno = EFAULT;
		return BADSIG;		/* error */
		}

	/* cancel pending signals */
	newsv.sv_handler = SIG_IGN;
	newsv.sv_mask = newsv.sv_onstack = 0;
	if ( sigvec( sig, &newsv, &oldsv ) != 0 )
		return BADSIG;		/* error */

	/* C language provides no good way to initialize handler[] */
	if ( !inited )			/* once only */
		{
		register int	i;

		for ( i = 1; i < NSIG; ++i )
			handler[i] = BADSIG;	/* initialize */

		++inited;
		}

	/* the first time for this sig, get state from the system */
	if ( (retval = handler[sig - 1]) == BADSIG )
		retval = oldsv.sv_handler;

	handler[sig - 1] = func;	/* keep track of state */

	if ( func == SIG_DFL )
		newsv.sv_handler = SIG_DFL;
	else if ( func != SIG_IGN )
		newsv.sv_handler = catchsig;	/* actual sig catcher */

	if ( func != SIG_IGN		/* sig already being ignored */
	  && sigvec( sig, &newsv, (struct sigvec *)0 ) != 0
	   )
		return BADSIG;		/* error */

	return retval;			/* previous handler */
	}


#ifdef accel	/* supplied by David Barto */

/* # bytes to skip at the beginning of __ret_eintr() function code: */
#define	OFFSET	0			/* start at beginning */

/* PC will be pointing at a syscall if it is to be restarted: */
typedef	unsigned char	opcode;		/* one byte long */
#define	SYSCALL		((opcode)0x18)	/* ACCEL syscall instruction */
/* restartable syscall codes: */
#define	READ		SYS_read
#define	WRITE		SYS_write
#define	IOCTL		SYS_ioctl
#define	WAIT		SYS_wait
#define	READV		SYS_readv
#define	WRITEV		SYS_writev

#else
#ifdef sel

/* # bytes to skip at the beginning of __ret_eintr() function code: */
#define	OFFSET	0			/* start at beginning */

/* PC will be pointing at a syscall if it is to be restarted: */
typedef unsigned short	opcode;		/* one halfword long */
#define	SYSCALL		((opcode)0xC806)/* Gould SVC instruction */
#define	INDEX		0x1000		/* Gould SVC vector index 1 */
/* restartable syscall codes: */
#define	READ		(INDEX | SYS_read)
#define	WRITE		(INDEX | SYS_write)
#define	IOCTL		(INDEX | SYS_ioctl)
#define	WAIT		(INDEX | SYS_wait)
#define	READV		(INDEX | SYS_readv)
#define	WRITEV		(INDEX | SYS_writev)

#else
#ifdef vax

/* # bytes to skip at the beginning of __ret_eintr() function code: */
#define	OFFSET	2			/* for VAX .word reg_mask */

/* PC will be pointing at a syscall if it is to be restarted: */
typedef unsigned char	opcode;		/* one byte long */
#define	SYSCALL		((opcode)0xBC)	/* VAX CHMK instruction */
#define	IMMEDIATE	((opcode)0x8F)	/* VAX immediate addressing */
/* restartable syscall codes: */
/* syscall codes < 64 are assembled in "literal" mode */
#define	READ		SYS_read
#define	WRITE		SYS_write
#define	IOCTL		SYS_ioctl
/* syscall codes > 63 are assembled in "immediate" mode; there is
   actually another 0 byte after the syscall code too */
#define	WAIT		SYS_wait
#define	READV		SYS_readv
#define	WRITEV		SYS_writev

#else

#include	"*** ERROR *** signal.c needs to be edited for CPU type"

#endif
#endif
#endif

#ifdef accel	/* supplied by David Barto */
#ifndef	sc_pc
#define	sc_pc	AC_fifo_ex
	/* This provides a compatible mechanism for examining and changing
	   the next instruction address.  Return from the signal handler
	   will adjust the interpret and fetch stages of the pipeline to
	   reflect any change to the execution address.
	   Note: changes to AC_fifo_interp and AC_fifo_fetch are ignored. */
#endif
#endif


/*ARGSUSED*/
static int
catchsig( sig, code, scp )		/* signal interceptor */
	register int		sig;	/* signal number */
	int			code;	/* code for SIGILL, SIGFPE */
	register struct sigcontext	*scp;	/* -> interrupted context */
	{
	register int		(*uhandler)();	/* user handler */
	register opcode		*pc;	/* for snooping instructions */
	register opcode		num;	/* system call number */
	struct sigvec		oldsv;	/* previous state */
	struct sigvec		newsv;	/* state being set */

	/* at this point, sig is blocked */

	uhandler = handler[sig - 1];

	/* most UNIXes usually want the state reset to SIG_DFL */
	if ( sig != SIGILL && sig != SIGTRAP /* && sig != SIGPWR */ )
		{
		handler[sig - 1] = newsv.sv_handler = SIG_DFL;
		newsv.sv_mask = newsv.sv_onstack = 0;
		(void)sigvec( sig, &newsv, &oldsv );
		}

	(void)sigsetmask( scp->sc_mask );	/* restore old mask */

	/* at this point, sig is not blocked, usually have SIG_DFL;
	   a longjmp may safely be taken by the user signal handler */

	(void)(*uhandler)( sig );	/* user signal handler */

	/* must now avoid restarting certain system calls */

	pc = (opcode *)scp->sc_pc;
	if ( pc[0] == SYSCALL )
		{
#ifdef accel
		num = (opcode)scp->AC_gen_reg[3];
#else	/* sel || vax */
		num = pc[1];
#ifdef vax
		if ( num == IMMEDIATE )
			num = pc[2];
#endif
#endif
		if ( num == READ || num == WRITE || num == IOCTL ||
		     num == WAIT || num == READV || num == WRITEV
		   )	{		/* fake return from __ret_eintr() */
#ifdef accel
			scp->AC_gen_reg[2] = -1;	/* return value */
			/* DAG -- Is this really necessary?
			   __ret_eintr() should do this. */
#endif
			scp->sc_pc = (int)__ret_eintr + OFFSET;
			}
		}

	/* return here restores interrupted context */
	}



More information about the Comp.unix.wizards mailing list