signal(2) emulation for 4.2BSD

gwyn%brl-vld at sri-unix.UUCP gwyn%brl-vld at sri-unix.UUCP
Fri Jan 13 22:35:57 AEST 1984


From:      Doug Gwyn (VLD/VMB) <gwyn at brl-vld>

I found the following problems in the previously posted emulation:
(1)  Incorrect initialization of the local handler history array;
(2)  Failure to update the history to SIG_DFL in the signal catcher.

The following code replaces the previous submission.  It does not
depend on the BRL kernel modification you have been hearing about
recently.  Recipients of the BRL UNIX System V emulation for 4.2BSD
should install corresponding corrections in the two signal.c source
files and rebuild things, especially the Bourne shell.


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

	last edit:	13-Jan-1984	D A Gwyn

	NOTE:  Although this module is VAX-specific, it should be
	possible to adapt it to other fairly clean 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();

extern		etext;
extern int	errno;

/* # bytes to skip at the beginning of C 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 */

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

static int	catchsig();
static int	ret_eintr();

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

/*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 opcode	*pc;		/* for snooping instructions */
	struct sigvec	newsv;		/* state being set */
	register int	(*uhandler)() = handler[sig-1];	/* user handler */

	/* at this point, sig is blocked */

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

	(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++ == SYSCALL
	  && (*pc == SYS_read || *pc == SYS_write || *pc == SYS_ioctl
	   || *pc++ == IMMEDIATE
	   && (*pc == SYS_wait || *pc == SYS_readv || *pc == SYS_writev)
	     )
	   )
		scp->sc_pc = (int)ret_eintr + OFFSET;

	/* return here restores interrupted context */
	}


static int
ret_eintr()				/* substitute for system call */
{
	errno = EINTR;
	return -1;
}



More information about the Comp.unix.wizards mailing list