/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: kern_sig.c 1.81 90/04/23 $"

/*
 * Software Signal handling.
 */

/* $Log:	kern_sig.c,v $
 */

/*
 * define JOB_CONTROL to avoid having to ifdef lots of supporting code
 */
#define _POSIX_JOB_CONTROL	1

#include "sys/param.h"
#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/mutex.h"
#include "sys/systm.h"
#include "sys/signal.h"
#include "sys/user.h"
#include "sys/vfs.h"
#include "sys/vnode.h"
#include "sys/timer.h"
#include "sys/proc.h"
#include "sys/conf.h"
#include "sys/buf.h"
#include "sys/vm.h"
#include "sys/acct.h"
#include "sys/uio.h"
#include "sys/var.h"
#include "sys/errno.h"

#include "sys/engine.h"

#include "sys/reg.h"
#include "sys/pte.h"
#include "sys/psl.h"
#include "sys/gate.h"
#include "sys/intctl.h"
#include "sys/plocal.h"
#include "sys/cmn_err.h"

/* #define DEBUG 1 */
#ifdef DEBUG
int	sig_debug = 0;		/* (0, 1, 2) */
extern int	ptrace_debug;
#endif

#include "sys/debug.h"

/*
 * POSIX B3.3.3.2
 * These signals are "safe" to not mask. (that is there behaviour is 
 * predictable). This include all supported POSIX signals.
 * It is recommended that implementation-defined signals be added.
 * However, there may be a good reason for a particular signal not to
 * be affected. For example, blocking or ignoring an implementaion-defined
 * signal may have undesirable side effects whereas the default action 
 * for that signal is harmless. In such case it would be preferable for 
 * such a signal to be excluded from the signal set returned by
 * sigfillset().
 *
 * We have decided to mask all signals.
 */
#define S_FILLSET (SIG_ALL)

/*
 * 4.2 BSD gsignal()
 * SVR3.1 signal()
 *	Send the specified signal to all processes with 'pgrp' as
 *	process group.
 */

signal(pgrp, sig)
	register int pgrp;
{
	register proc_t *p;
	spl_t 		s_ipl;

#ifdef DEBUG
	if (sig_debug) 
		printf("signal: signal(pgrp:0x%x, sig:%d)\n", pgrp, sig);
#endif

	if (pgrp == 0)
		return;
	s_ipl = p_lock(&proc_list, SPL_PROC_LIST);
	for (p = &proc[1]; p < v.ve_proc; p++)
		if (p->p_pgrp == pgrp) {
			(void) p_lock(&p->p_state, SPLHI);
			if (p->p_pgrp == pgrp)
				lpsignal(p, sig);
			v_lock(&p->p_state, SPL_PROC_LIST);
		}
	v_lock(&proc_list, s_ipl);
}


/*
 *	Send the specified signal to all processes in the process group
 *	linkage.
 *	session must be locked by caller.
 */

gsignal(p, sig)
	register proc_t *p;
{
	register proc_t *q;
	spl_t 		spl;

#ifdef DEBUG
	if (sig_debug) 
		printf("gsignal: gsignal(pgrp:0x%x, sig:%d)\n", p, sig);
#endif

	q = p;
	do {
		spl = p_lock(&q->p_state, SPLHI);
		lpsignal(q, sig);
		v_lock(&q->p_state, spl);
		q = q->p_pgrpf;
	} while(q != p);
}


/*
 * psignal()
 *	Post a signal to a process.
 *
 * Send the specified signal to the specified process.
 *
 * Lock process state and call lpsignal. 
 */

psignal(p, sig)
	proc_t *p;
	int 	sig;
{
	register spl_t s_ipl;

	s_ipl = p_lock(&p->p_state, SPLHI);
	lpsignal(p, sig);
	v_lock(&p->p_state, s_ipl);
}


/*
 * lpsignal()
 *	Locked psignal. 
 *
 * Send the specified signal to the specified process.
 *
 * A signal sets a flag that asks the process to do something to itself,
 * possibly forcing it off a semaphore or setrun a stopped process.
 *
 * The process state (p->state) is locked by the caller at SPLHI.
 */

lpsignal(p, sig)
	register proc_t	*p;
	register int 	sig;
{
	register void (*action)();
	register sigset_t mask;

#ifdef DEBUG
	if (sig_debug) {
		if (u.u_procp)
			printf("lpsignal:[proc %d pid %d] lpsignal(p:0x%x[%d]pid:%d, sig:%d)\n", u.u_procp-proc, u.u_procp->p_pid, p, p-proc, p->p_pid, sig);
		else
			printf("lpsignal:[no proc] lpsignal(p:0x%x[%d]pid:%d, sig:%d)\n", p, p-proc, p->p_pid, sig);
	}
#endif

	if ((unsigned)sig > NSIG)
		return;
	mask = sigmask(sig);

#ifdef DEBUG
	if (sig_debug && (u.u_procp == p) && (sig == SIGSYS))
		printf("SIGSYS u.u_code = 0x%x\n", u.u_code);
#endif

	/*
	 * Having p_state locked synchronizes with
	 * setsigvec changing p_sigignore, p_sigcatch and p_sig.
	 * p_sig always needs to be protected with a lock since it
	 * may be changed by psignal as well as the process itself.
	 */

	/*
	 * If proc is traced, always give parent a chance.
	 */
	if (p->p_flag & SDBG)
		action = SIG_DFL;
	else {
		if (SIGM_ISMEMBER(p->p_sigmask, mask))
			action = SIG_HOLD;
		else if (SIGM_ISMEMBER(p->p_sigcatch, mask))
			action = SIG_CATCH;
		else if (SIGM_ISMEMBER(p->p_sigignore, mask)) {
#ifdef DEBUG
			if (sig_debug>1)
				printf("lpsignal: ignored (p_sigignore:0x%x)\n", p->p_sigignore);
#endif
			/*
			 * If the signal is being ignored,
			 * and is not blocked.
			 * then we forget about it immediately.
			 * except SIGCONT and SIGKILL.
			 */
			if ((sig != SIGCONT) && (sig != SIGKILL))
				return;
			action = SIG_IGN;
		} else
			action = SIG_DFL;
	}

	SIGM_ADDSET(p->p_sig , mask);
#ifdef DEBUG
	if (sig_debug>1) 
		printf("lpsignal: p_sig is now 0x%x p_sigmask is 0x%x\n", p->p_sig, p->p_sigmask);
#endif
	switch (sig) {
	case SIGTERM:
		if ((p->p_flag&SDBG) || action != SIG_DFL)
			break;
		/* fall into ... */
	case SIGKILL:
		if (p->p_nice > NZERO)
			p->p_nice = NZERO;
		break;
	case SIGCONT:
		SIGM_DELSET(p->p_sig, STOPS);
		break;
	case SIGSTOP:
	case SIGTSTP:
	case SIGTTIN:
	case SIGTTOU:
		SIGM_DELSET(p->p_sig, sigmask(SIGCONT));
		break;
	}

#ifdef DEBUG
	if (sig_debug>1) {
		printf("lpsignal: p_sig is 0x%x p_stat is 0x%x ", p->p_sig, p->p_stat);
		switch ((int)action) {
		case (int)SIG_DFL:
			printf("SIG_DFL\n");
			break;
		case (int)SIG_HOLD:
			printf("SIG_HOLD\n");
			break;
		case (int)SIG_CATCH:
			printf("SIG_CATCH\n");
			break;
		default:
			printf("\n");
			break;
		}
	}
#endif
	/*
	 * Defer further processing for signals which are held.
	 */
	if ((action == SIG_HOLD) && (sig != SIGCONT))
		return;

	switch (p->p_stat) {

	case SSLEEP:
		/*
		 * If process is sleeping at negative priority
		 * we can't interrupt the sleep... the signal will
		 * be noticed when the process returns through
		 * trap() or syscall().
		 */
		if ((p->p_flag & SNWAKE) || (action == SIG_HOLD)) 
			return;
		/*
		 * Process is sleeping and traced... make it runnable
		 * so it can discover the signal in issig() and stop
		 * for the parent.
		 */
		if (p->p_flag&SDBG)
			goto run;
		switch (sig) {

		case SIGSTOP:
		case SIGTSTP:
		case SIGTTIN:
		case SIGTTOU:
			/*
			 * These are the signals which by default
			 * stop a process.
			 */
			if (action != SIG_DFL)
				goto run;
			/*
			 * POSIX B.3.3.1.4
			 * An orphaned process that recievs a terminal-rel-
			 * ated stop signals (SIGTSTP, SIGTTIN, SIGTTOU but
			 * not SIGSTOP) must not be allowed to stop.
			 */
			if ((sig != SIGSTOP) && (p->p_flag & SORPH)) {
				SIGM_DELSET(p->p_sig, mask);
				return;
			}
			/*
			 * If a child in vfork(), stopping could
			 * cause deadlock.
			 */
			if (p->p_flag&SVFORK)
				return;
			/*
			 * Kick process off of semaphore, issig will
			 * perform the stop. Code that sleeps at pri > PZERO
			 * is assumed to tolerate this. This is why sigpause
			 * p_sema is in a loop...
			 */
			goto run;

		case SIGPWR:
		case SIGCONT:
		case SIGWINCH:
		case SIGPOLL:
			/*
			 * These signals are special in that they
			 * don't get propogated... if the process
			 * isn't interested, forget it.
			 */
			if (action != SIG_DFL)
				goto run;
			SIGM_DELSET(p->p_sig, mask); /* take it away */
			return;

		case SIGCLD:
			/*
			 * If the process is ignoring SIGCLD and he/she also
			 * happens to be sleeping (on a semaphore), then
			 * we do not want to force_v_sema the process since
			 * this causes issig to return NULL which causes the
			 * process to return from a p_sema (or p_sema_v_lock)
			 * without having acquired the semaphore.  We
			 * only want to SCHEDULE the SIGCLD to be handled
			 * by issig before the process returns to the user.
			 * But, if the action is SIG_CATCH, we definitely
			 * want to force the process to wake up (note that in
			 * this case issig does not return NULL).  Note that
			 * because of the SYSV side effects of SIGCLD with
			 * with action set to SIGIGN, p_sigignore will not
			 * indicate SIGCLD.  Thus, the "action" local variable
			 * will be set to SIG_DFL early in this routine when
			 * SIGCLD has been ignored by the process.
			 */
			if (action != SIG_DFL)
				goto run;
			return;

		default:
			/*
			 * All other signals cause the process to run
			 */
			goto run;
		}
		/*NOTREACHED*/

	case SSTOP:
		/*
		 * If traced process is already stopped,
		 * then no further action is necessary.
		 */
		if (p->p_flag&SDBG)
			return;
		switch (sig) {
		case SIGKILL:
		case SIGCONT:
			/*
			 * Kill signal always sets processes running.
			 * Cont signal always sets the process running.
			 */
			setrun(p);
			return;
		case SIGSTOP:
		case SIGTSTP:
		case SIGTTIN:
		case SIGTTOU:
			/*
			 * Already stopped, don't need to stop again.
			 * (If we did the shell could get confused.)
			 */
			SIGM_DELSET(p->p_sig, mask); /* take it away */
			return;
		default:
			/*
			 * Don't setrun the process as its not to
			 * be unstopped by the signal alone.
			 */
			return;
		}
		/*NOTREACHED*/

	case SRUN:
	case SONPROC:
		/*
		 * SRUN, SONPROC do nothing with the signal.
		 * Except kick ourselves, if we are SONPROC.
		 * If just posted a stopping signal, clear p_slptime
		 * to more accurately compute time stopped.
		 *
		 * Note: Must lock runq and check to verify still SONPROC
		 */
		if (action == SIG_HOLD)
			return;
		if (SIGM_ISMEMBER(p->p_sig, STOPS) != 0)
			p->p_slptime = 0;
		VOID_P_GATE(G_RUNQ);
		if (p->p_stat == SONPROC)
			nudge(PUSER, &engine[p->p_engno]);
		V_GATE(G_RUNQ);
		/* fall into ... */
	default:
		/*
		 * SIDL, SZOMB do nothing with the signal.
		 */
#ifdef DEBUG
		if (sig_debug>1)
			printf("lpsignal: end %d\n", p-proc);
#endif
		return;
	}
	/*NOTREACHED*/
run:
#ifdef DEBUG
	if (sig_debug>1)
		printf("lpsignal: run %d\n", p-proc);
#endif
	force_v_sema(p);
}


/*
 * issig()
 *	Set up process to handle signal.  Handles stops directly.
 *
 * Returns:
 *	GOTSIG		if the current process has a signal to process.
 *	STPGOTSIG	if the current process has seen a stop but
 *			still has a signal to process.
 *	NOSIG		if the current process has no signal to process.
 *	STOPPED		if stop signal was processed and process has no other
 *			signal to process. 
 *
 * The signal to process is put in p_cursig.
 *
 * This is asked at least once each time a process enters the
 * system (though this can usually be done without actually
 * calling issig by checking the pending signal masks.)
 * The held lock is dropped only if the process had been STOPPED.
 *
 * p_state is assumed locked by the caller at SPLHI.
 *
 * issig is call with a held_lock (not p_state) from:
 *	p_sema_v_lock()
 *	ctty_read()
 *	ctty_write()
 *
 * and is called with no lock from:
 *	p_sema() 	before and after switch()
 *	trap()		just before returning to user land.
 *	syscall()	just before returning to user land.
 *	streamio()	on a background block.
 */

issig(held_lock)
	lock_t	*held_lock;
{
	register proc_t	*p;
	register proc_t *child;
	register proc_t *sib;
	register int sig;
	register sigset_t sigbits;
	register sigset_t mask;
	register proc_t *elder;
	int	stopped;
	int	sigwoke;

	p = u.u_procp;
#ifdef DEBUG
	if (sig_debug>1)
		printf("issig: p=0x%x (%d) p_cursig=0x%x p_sig=0x%x p_sigmask=0x%x\n", p, p-proc, p->p_cursig, p->p_sig, p->p_sigmask);
#endif
	/*
	 * avoid duplicate calculations.
	 */
	if (p->p_cursig) {
#ifdef DEBUG
		if (sig_debug>1)
			printf("isigg:already GOT sigs %d\n", p->p_cursig);
#endif
		return (GOTSIG);
	}
	stopped = 0;
	for (;;) {
		sigbits = p->p_sig;
		SIGM_DELSET(sigbits, p->p_sigmask);

		if ((p->p_flag&SDBG) == 0)
			SIGM_DELSET(sigbits, p->p_sigignore);
		if (p->p_flag&SVFORK)
			SIGM_DELSET(sigbits, STOPS);
		if (sigbits == 0)
			break;
		sig = ffs(sigbits);
		mask = sigmask(sig);
		SIGM_DELSET(p->p_sig, mask);	/* take the signal! */
		p->p_cursig = sig;
#ifdef DEBUG
		if (sig_debug>1)
			printf("issig: p_cursig = %d\n", p->p_cursig);
#endif

		/*
		 * If being debugged (traced) and signal not being
		 * passed thru, stop and tell debugger.
		 */

		if ((p->p_flag&SDBG) && !SIGM_ISMEMBER(u.u_sigpass, mask)
		    && (p->p_flag&SVFORK) == 0 ) {
#ifdef DEBUG
			if (ptrace_debug)
				printf("issig:pid:%d DEBUG\n", p->p_pid);
#endif
			if (p->p_ppid == 1 && p->p_flag & STRC) {
				/*
				 * Parent of ptraced child has
				 * died; arrange for child to die.
				 */
				if (p->p_cursig == 0)
					p->p_cursig = SIGKILL;
				goto send;
			}
			/*
			 * Save SIGWOKE in case procxmt() manages to
			 * get it turned off.
			 */
			sigwoke = p->p_flag & SIGWOKE;
			/*
			 * If traced by a multi-process debugger,
			 * let it know about the signal.
			 * When continue, potentially handle old
			 * ptrace debugger (if signal still posted).
			 */
			if (p->p_flag & SMPTRC) {
#ifdef DEBUG
				if (ptrace_debug)
					printf("issig:pid:%d mpt on sig %d\n", p->p_pid, sig);
#endif
				stopped = 1;
				if (held_lock) {
					v_lock(held_lock, SPLHI);
					held_lock = NULL;
				}
				mpt_stop(sig);
				sig = p->p_cursig;
				mask = (sig ? sigmask(sig) : 0);
#ifdef DEBUG
				if (ptrace_debug)
					printf("issig:pid:%d mpt sig is now %d\n", p->p_pid, sig);
#endif
			}
			/*
			 * If (old style) traced, always stop, and stay
			 * stopped until released by the parent.
			 * STRCSTP for things like unselect().
			 */
			if ((p->p_flag & STRC) && sig) {
#ifdef DEBUG
				if (ptrace_debug)
					printf("issig:pid:%d pt on sig %d\n", p->p_pid, sig);
#endif
				stopped = 1;
				if (held_lock) {
					v_lock(held_lock, SPLHI);
					held_lock = NULL;
				}
				pt_stop(sig);
				sig = p->p_cursig;
				mask = (sig ? sigmask(sig) : 0);
#ifdef DEBUG
				if (ptrace_debug)
					printf("issig:pid:%d pt sig is now %d\n", p->p_pid, sig);
#endif
			}
			p->p_flag |= sigwoke;

			/*
			 * If the traced bit got turned off,
			 * then put the signal taken above back into p_sig
			 * and go back up to the top to rescan signals.
			 * This ensures that p_sig* and u_signal are consistent.
			 */
			if ((p->p_flag&SDBG) == 0) {
				SIGM_ADDSET(p->p_sig, mask);
				continue;
			}

#ifdef DEBUG
			if (ptrace_debug)
				printf("issig:pid:%d end DEBUG\n", p->p_pid);
#endif
			/*
			 * If parent wants us to take the signal,
			 * then it will leave it in p->p_cursig;
			 * otherwise just look for signals again.
			 */
			sig = p->p_cursig;
			if (sig == 0)
				continue;

			if (p->p_ppid == 1) {
				goto send;
			}

			/*
			 * If signal is being masked put it back
			 * into p_sig and look for other signals.
			 */
			mask = sigmask(sig);
			if (SIGM_ISMEMBER(p->p_sigmask, mask)) {
				SIGM_ADDSET(p->p_sig, mask);
				continue;
			}
		}

		switch ((int)(u.u_sa[sig].sa_handler)) {

		case (int)SIG_DFL:
			switch (sig) {

			case SIGTSTP:
			case SIGTTIN:
			case SIGTTOU:
				/*
				 * POSIX 1003.1 3.3.1.3
				 * If a process is a member of an 
				 * orphaned process group shall not be 
				 * allowed to stop in responce to the
				 * SIGSTSTP, SIGTTIN, or SIGTTOU signals. 
				 * In cases where delivery of one of these
				 * signals would stop such a
				 * process, the signal shall be discarded.
		 		*/
				if (p->p_flag & SORPH) {
					p->p_cursig = 0;
					continue;
				}
				/* fall into ... */

			case SIGSTOP:
				/*
				 * If process being debugged, "handle"
				 * above; actually, will ignore signal
				 * since not placed back in p_sig --
				 * kept for backwards compatilibity.
				 */
				if (p->p_flag&SDBG)
					continue;
				stop(p);
				stopped = 1;
				if (held_lock != NULL) {
					v_lock(held_lock, SPLHI);
					held_lock = NULL;
				}
				/* swtch does v_lock on p_state */
				swtch(p);
				/* Relock p_state */
				(void) p_lock(&p->p_state, SPLHI);
				continue;

			case SIGPWR:
			case SIGCONT:
			case SIGCLD:
			case SIGWINCH:
			case SIGPOLL:	
				/*
				 * These signals are normally not
				 * sent if the action is the default.
				 */
#ifdef DEBUG
				if (sig_debug>1)
					printf("isigg: signal ignored\n");
#endif
				continue;		/* == ignore */

			default:
				goto send;
			}
			/*NOTREACHED*/

		case (int)SIG_IGN:
			if (sig == SIGCLD) {
				/*
				 * Note SIGCLD is not put in p_sigignore
				 * even though it is ignored.
				 */
				elder = NULL;
				for(child=p->p_child;child;child=sib) {
#ifdef DEBUG
					if (sig_debug)
					 printf("issig:ignored SIGCLD %d->%d state 0x%x\n",
						p->p_pid, child->p_pid,
						child->p_stat);
#endif
					sib = child->p_sibling;
					if (child->p_stat == SZOMB) {
#ifdef DEBUG
						if (sig_debug)
							printf("issig:ignored child being killed (proc:%d pid:%d elder:%d sibling:%d)\n", child-proc, child->p_pid, elder?elder->p_pid:0, child->p_sptr?child->p_sptr->p_pid:0);
#endif
						freeproc(child, elder, 0);
					} else
						elder = child;
				}
				/*
				 * Now ignore it.
				 */
				p->p_cursig = 0;
				continue;
			}
			/*
			 * Process has been awakened by a SIGCONT
			 * that has been ignored.
			 */
			if (sig == SIGCONT) {
				p->p_cursig = 0;
				continue;
			}
			/*
			 * Process is exiting with SIGKILL = SIG_IGN.
			 * and will never reach psig().
			 */
			if (sig == SIGKILL)
				goto send;
			/*
			 * Masking above should prevent us
			 * ever trying to take action on an
			 * ignored signal, unless process is traced.
			 */
#ifdef DEBUG
			if (!(p->p_flag & SDBG))
				printf("Signal %d\n", sig);
#endif
			ASSERT(p->p_flag & SDBG, "issig\n");
			/*
			 *+ An ignored signal was about to be
			 *+ delivered to a process that isn't
			 *+ being traced.
			 */
			continue;
		default:
			/*
			 * This signal has an action, let
			 * psig process it.
			 */
			goto send;
		}
		/*NOTREACHED*/
	}
	/*
	 * Didn't find a signal to send.
	 */
	p->p_cursig = 0;
	if (stopped)
		return(STOPPED);
	return(NOSIG);

send:
	/*
	 * Let psig process the signal.
	 */
#ifdef DEBUG
	if (sig_debug)
		printf("isigg: GOT sigs %d\n", p->p_cursig);
#endif
	if (stopped)
		return(STPGOTSIG);
	return(GOTSIG);
}


/*
 * stop()
 *	Put the argument process into the stopped
 *	state and notify the parent via vsema and/or signal.
 *
 * Process state: p->p_state is locked by the caller at SPLHI.
 */

stop(p)
	register proc_t *p;
{
	p->p_stat = SSTOP;
	p->p_flag &= ~SWTED;
	v_event(&p->p_pptr->p_zombie);
	/*
	 * If process is not traced, send signal to parent.
	 * if so set. (no lock held).
	 */
	if (p->p_pptr->p_flag & SWSIG) {
#ifdef DEBUG
		if (sig_debug)
			printf("stop: SIGCLD sent to perent\n");
#endif
		psignal(p->p_pptr, SIGCLD);
	}
}

/*
 * psig()
 *	Perform the action specified by the current signal.
 *
 * The usual sequence is:
 *	if (issig())
 *		psig();
 *
 * The signal bit has already been cleared by issig,
 * and the current signal number stored in p->p_cursig.
 * p_state is not normaly locked.
 */

psig()
{
	register proc_t *p;
	register int sig;
	register sigset_t mask;
	register sigset_t returnmask;
	void	(*action)();
	extern	void	sendsig();
	extern	void	bcs_sendsig();

	p = u.u_procp;
	sig = p->p_cursig;
	p->p_cursig = 0;
#ifdef DEBUG
	if (sig_debug) {
		printf("psig: cursig %d\n", sig);
		if (sig == SIGSEGV) {
			printf("SIGSEGV code=0x%x segvcode=0x%x\n",
						    u.u_code, u.u_segvcode);
		 }
	}
#endif
	ASSERT_DEBUG(((sig != 0)&&(sig < NSIG)), "psig: bad sig");
	if ((p->p_flag & SDBG) && (sig == SIGKILL))
		exit(SIGKILL);
	mask = sigmask(sig);
	action = u.u_sa[sig].sa_handler;
	if (action != SIG_DFL) {
		ASSERT_DEBUG(action != SIG_IGN && (SIGM_ISMEMBER(p->p_sigmask, mask) == 0), "psig:bad action");
		/*
		 * Set the new mask value and also defer further
		 * occurences of this signal (unless we're simulating
		 * the old signal facilities). 
		 */
		returnmask = p->p_sigmask;
		if (u.u_sa[sig].sa_flags & SA_NOHOLD) {
			if (sig != SIGILL && sig != SIGTRAP && sig != SIGPWR) {
				u.u_sa[sig].sa_handler = SIG_DFL;
				SIGM_DELSET(p->p_sigcatch, mask);
			}
			mask = 0;
		} else {
			(void)p_lock(&p->p_state, SPLHI);
			SIGM_ADDSET(p->p_sigmask, mask);
			SIGM_ADDSET(p->p_sigmask, u.u_sa[sig].sa_mask);
			v_lock(&p->p_state, SPL0);
		}
		u.u_pst.ps_signal++;
		if (u.u_ptype == BCS_PROC)
			bcs_sendsig(action, sig, returnmask);
		else
			sendsig(action, sig, returnmask);
		return;
	}
	switch (sig) {

	case SIGILL:
	case SIGIOT:	/* SIGABRT */
	case SIGBUS:
	case SIGQUIT:
	case SIGTRAP:
	case SIGEMT:
	case SIGFPE:
	case SIGSEGV:
	case SIGSYS:
		u.u_arg[0] = sig;
		if (core())
			sig += 0200;
	}
	exit(sig);
}


/*
 * Wait for someone else to wake us up.
 * Use the passed mask as sig_mask after saving the current mask;
 * Do it here, atomicaly, with p_state locked.
 */
sigpause(mask)
	sigset_t	mask;
{
	proc_t	*p = u.u_procp;
	/*
	 * When returning from sigpause, we want
	 * the old mask to be restored after the
	 * signal handler has finished.  Thus, we
	 * save it here.
	*/
	u.u_oldmask = p->p_sigmask;
	/*
	 * insure that oldmask is not zero
	 */
	SIGM_ADDSET(u.u_oldmask, NULLSIG);
#ifdef DEBUG
	if (sig_debug>1)
		printf("sigpause: sigmask=0x%x new:0x%x p_sig=0x%x sigignore=0x%x\n", p->p_sigmask, mask, p->p_sig, p->p_sigignore);
#endif
	(void)p_lock(&p->p_state, SPLHI);
	p->p_sigmask = mask;
	v_lock(&p->p_state, SPL0);
	u.u_r.r_val1 = 0;
	pause();
}


sigstack()
{
	register struct a {
		struct	sigstack *nss;
		struct	sigstack *oss;
	} *uap = (struct a *)u.u_ap;
	struct sigstack ss;

	if (uap->oss) {
		ss.ss_sp = u.u_altstack.sas_sp;
		ss.ss_onstack = u.u_altstack.sas_onstack;
		u.u_error = copyout((caddr_t)&ss, (caddr_t)uap->oss, 
		    				     sizeof (struct sigstack));
		if (u.u_error)
			return;
	}
	if (uap->nss) {
		u.u_error =
			copyin((caddr_t)uap->nss, (caddr_t)&ss, 
						    sizeof (struct sigstack));
		if (u.u_error == 0) {
			u.u_altstack.sas_sp = ss.ss_sp;
			u.u_altstack.sas_onstack = ss.ss_onstack;
		}
	}
}

/* 
 * Ssig() is the common entry for all signal routines
 *  SVR3  sigset, sighold, sigrelse, sigignore and sigpause 
 *  POSIX sigaction, sigprocmask, sigsuspend, sigemptyset and sigfillset 
 *
 * Lock proc-state (even though only process changes its
 * own p_sigmask), to avoid race with lpsignal() deciding
 * action=SIG_HOLD for a signal we're enabling here.
 * If this were to happen then p->p_sig may be set after
 * we return to user mode, and hence miss the signal.
 * (p_sig is checked at the end of syscall()).
 * Therefore by locking we are insured that from our prospective
 * that p->p_sig reflects the current value of p->sigmask when 
 * we have the lock and that from the point of view of
 * lpsignal() it sets p->p_sig according to a consistant
 * value of p->p_sigmask and possible nudges us out of the lock.
 * This lock is only needed when the mask is enabled.
 */

ssig()
{
	register int	a;
	register proc_t *p;
	register struct a {
		int	signo;
		union {
			struct {
				void	(*fun)();
				void	(*sigreturn)();
			} signal;
			struct {
				struct	sigaction	*act;
				struct	sigaction	*oact;
				void	(*sigreturn)();
			} sigaction;
			struct {
				sigset_t	*set;
				sigset_t	*oset;
			} sigops;
		} arg;
	} *uap;
	sigset_t 		mask;
	sigset_t		set;
	struct	sigaction	siga;

	uap = (struct a *)u.u_ap;
	a = uap->signo & SIGNO_MASK;
	p = u.u_procp;
#ifdef DEBUG
	if (sig_debug>1)
		printf("[proc %d] ssig(signo=0x%x, 0x%x, 0x%x)\n", p-proc, uap->signo, uap->arg.signal.fun, uap->arg.signal.sigreturn);
#endif
	/*
	 * Range check any passed signal numbers
	 */
	switch (uap->signo & ~SIGNO_MASK) {
	case 0:
	case SIGDEFER:
	case SIGHOLD:
	case SIGRELSE:
	case SIGIGNORE:
	case SIGPAUSE:
		/*
		 * for SVVD compliance
		 */
		if (a == SIGKILL) {
			u.u_error = EINVAL;
			return;
		}
		/* fall though */
	case SIGACTION:
		/*
		 * POSIX 3.3.4.4
		 * The value of the sig argument is an invalid or 
		 * unsupported signal number.
		 */
		if ((a <= 0) || (a >= NSIG)) {
			u.u_error = EINVAL;
			return;
		}
		mask = sigmask(a);
		if (SIGM_ISMEMBER(mask, SIG_NOT)) {
			u.u_error = EINVAL;
			return;
		}
		break;
	case SIGADDSET:
	case SIGDELSET:
	case SIGISMEMBER:
		mask = sigmask(a);
		break;
	}
	switch (uap->signo & ~SIGNO_MASK) {

	case SIGHOLD:	/* sighold */
#ifdef DEBUG
		if (sig_debug)
			printf("ssig: SIGHOLD signo=%d\n", a);
#endif
		if (SIGM_ISMEMBER(mask, CANTMASK)) {
			u.u_error = EINVAL;
			return;
		}
		(void)p_lock(&p->p_state, SPLHI);
		SIGM_ADDSET(p->p_sigmask, mask);
		v_lock(&p->p_state, SPL0);
		u.u_r.r_val1 = 0;
		return;

	case SIGRELSE:	/* sigrelse */
#ifdef DEBUG
		if (sig_debug)
			printf("ssig: SIGRELSE signo=%d\n", a);
#endif
		(void)p_lock(&p->p_state, SPLHI);
		SIGM_DELSET(p->p_sigmask, mask);
		v_lock(&p->p_state, SPL0);
		u.u_r.r_val1 = 0;
		return;

	case SIGIGNORE:	/* signore */
#ifdef DEBUG
		if (sig_debug)
			printf("ssig: SIGIGNORE signo=%d mask=0x%x\n", a, mask);
#endif
		if (SIGM_ISMEMBER(mask, CANTIGNORE)) {
			u.u_error = EINVAL;
			return;
		}
		u.u_sa[a].sa_handler = SIG_IGN;
		(void)p_lock(&p->p_state, SPLHI);
		if (a != SIGCLD)
			SIGM_ADDSET(p->p_sigignore, mask);
		SIGM_DELSET(mask, CANTMASK);
		/*
		 * Set not to catch
		 * Ignore current pending signal.
		 */
		SIGM_DELSET(p->p_sigcatch, mask);
		SIGM_DELSET(p->p_sig, mask);
		/*
		 * delete current blocked signal
		 */
		SIGM_DELSET(p->p_sigmask, mask);
		v_lock(&p->p_state, SPL0);
#ifdef DEBUG
		if (sig_debug>1)
			printf("ssig: p_sigignore:0x%x p_sigmask:0x%x\n", p->p_sigignore, p->p_sigmask);
#endif
		u.u_r.r_val1 = 0;
		return;

	case SIGPAUSE:	/* sigpause */
#ifdef DEBUG
		if (sig_debug)
			printf("ssig: SIGPAUSE signo=%d\n", a);
#endif
		/*
		 * let sigpause change p_sigmask.
		 * by deleting the single sig_no from it.
		 * Note that unlike B.S.D sigpause() or POSIX sigsuspend()
		 * the signal is released if caught!
		 */
		(void)p_lock(&p->p_state, SPLHI);
		SIGM_DELSET(mask, CANTMASK);
		SIGM_DELSET(p->p_sigmask, mask);
		set = p->p_sigmask;
		v_lock(&p->p_state, SPL0);
		SIGM_DELSET(set, mask);
		SIGM_DELSET(set, CANTMASK);
		sigpause(set);
		return;

	case SIGDEFER:		/* sigset */
#ifdef DEBUG
		if (sig_debug)
			printf("ssig: SIGDEFER signo=%d\n", a);
#endif
		if ((uap->arg.signal.fun != SIG_DFL) && (uap->arg.signal.fun != SIG_IGN) && (uap->arg.signal.fun != SIG_HOLD)) {
			u.u_sa[a].sa_flags |= SA_SIGSET;
			u.u_sa[a].sa_flags &= ~SA_NOHOLD;
		}

		/*
		 * set return values.
		 */
		if (SIGM_ISMEMBER(p->p_sigmask, mask))
			u.u_r.r_val1 = (int) SIG_HOLD;
		else
			u.u_r.r_val1 = (int) (u.u_sa[a].sa_handler);

		/*
		 * set p_sigmask, p_sigignore and p_sigcatch masks.
		 */
		(void)p_lock(&p->p_state, SPLHI);
		u.u_sigreturn = uap->arg.signal.sigreturn;
		switch ((int)uap->arg.signal.fun) {
		case (int)SIG_IGN:
			if (SIGM_ISMEMBER(mask, CANTIGNORE)) {
				u.u_error = EINVAL;
			 	v_lock(&p->p_state, SPL0);
				return;
			}
			if (a != SIGCLD)
				SIGM_ADDSET(p->p_sigignore, mask);
			SIGM_DELSET(p->p_sigcatch, mask);
			u.u_sa[a].sa_handler = SIG_IGN;
			SIGM_DELSET(p->p_sigmask, mask);
			break;
		case (int)SIG_HOLD:
			SIGM_DELSET(mask, CANTMASK);
			SIGM_DELSET(p->p_sigignore, mask);
			SIGM_ADDSET(p->p_sigcatch, mask);
			SIGM_ADDSET(p->p_sigmask, mask);
			break;
		case (int)SIG_DFL:
			SIGM_DELSET(p->p_sigignore, mask);
			SIGM_DELSET(p->p_sigcatch, mask);
			u.u_sa[a].sa_handler = SIG_DFL;
			SIGM_DELSET(p->p_sigmask, mask);
			break;
		default:
			if (SIGM_ISMEMBER(mask, CANTCATCH)) {
				u.u_error = EINVAL;
			 	v_lock(&p->p_state, SPL0);
				return;
			}
			SIGM_DELSET(p->p_sigignore, mask);
			SIGM_ADDSET(p->p_sigcatch, mask);
			u.u_sa[a].sa_handler = uap->arg.signal.fun;
			SIGM_DELSET(p->p_sigmask, mask);
			break;
		}
		u.u_sa[a].sa_flags &= ~SA_NOHOLD;
		v_lock(&p->p_state, SPL0);
		break;

	case 0:	/* signal systemV and ANSI-C */
#ifdef DEBUG
		if (sig_debug)
			printf("ssig: SIGNAL signo=%d fun = 0x%x \n", a, uap->arg.signal.fun);
#endif
		/*
		 * Old style signals set to SIG_DLF and not held.
		 */
		u.u_sa[a].sa_flags |= SA_NOHOLD;
		u.u_sa[a].sa_flags &= ~SA_SIGSET;
		/*
		 * Return the old values.
		 */
		(void)p_lock(&p->p_state, SPLHI);
		u.u_r.r_val1 = (int) u.u_sa[a].sa_handler;

		/*
		 * come what may, cancel current signal.
		 */
		SIGM_DELSET(p->p_sig, mask);

		/*
		 * set p_sigmask, p_sigignore and p_sigcatch masks.
		 */
		switch ((int)uap->arg.signal.fun) {
		case (int)SIG_IGN:
			if (SIGM_ISMEMBER(mask, CANTMASK)) {
				u.u_error = EINVAL;
			 	v_lock(&p->p_state, SPL0);
				return;
			}
			SIGM_DELSET(mask, CANTIGNORE);
			if (a != SIGCLD)
				SIGM_ADDSET(p->p_sigignore, mask);
			SIGM_DELSET(p->p_sigcatch, mask);
			break;
		case (int)SIG_HOLD:
			v_lock(&p->p_state, SPL0);
			u.u_error = EINVAL;
			return;
		case (int)SIG_DFL:
			SIGM_DELSET(p->p_sigignore, mask);
			SIGM_DELSET(p->p_sigcatch, mask);
			break;
		default:
			if (SIGM_ISMEMBER(mask, CANTCATCH)) {
				u.u_error = EINVAL;
			 	v_lock(&p->p_state, SPL0);
				return;
			}
			SIGM_DELSET(p->p_sigignore, mask);
			SIGM_ADDSET(p->p_sigcatch, mask);
			break;
		}
		/*
		 * set the signal.
		 */
		u.u_sigreturn = uap->arg.signal.sigreturn;
		u.u_sa[a].sa_handler = uap->arg.signal.fun;
		v_lock(&p->p_state, SPL0);
		break;

	case SIGEMPTYSET:	/* sigsinitset */
		/* 
		 * POSIX 3.3.3.1
		 * The sigemptyset function initializes the signal set
		 * pointed to by the argument "set", such that all signals
		 * defined in this standard are excluded.
		 */
#ifdef DEBUG
		if (sig_debug)
			printf("ssig: SIGEMPTYSET supported 0x%x\n", ~S_FILLSET);
#endif
		if (suword((caddr_t)uap->arg.sigops.set, ~S_FILLSET))
			u.u_error = EFAULT;
		return;
	case SIGFILLSET:	/* sigsfillset */
		/* 
		 * POSIX 3.3.3.1
		 * The sigfillset function initializes the signal set
		 * pointed to by the argument "set", such that all signals
		 * defined in this standard are included.
		 */
#ifdef DEBUG
		if (sig_debug)
			printf("ssig: SIGFILLSET supported 0x%x\n", S_FILLSET);
#endif
		if (suword((caddr_t)uap->arg.sigops.set, S_FILLSET))
			u.u_error = EFAULT;
		return;
	case SIGADDSET:	/* sigaddset */
		/* 
		 * POSIX 3.3.3.1
		 * The sigaddset() function adds the individual signal
		 * specified by the value of the argument "signo" from
		 * the signal set pointed to by the argument "set".
		 */
		if (u.u_error = copyin((caddr_t)uap->arg.sigops.set, 
					      (caddr_t)&set, sizeof(sigset_t)))
			return;
		SIGM_ADDSET(set, mask);
		u.u_error = copyout((caddr_t)&set, (caddr_t)uap->arg.sigops.set,
					sizeof(sigset_t));
		return;
	case SIGDELSET:	/* sigdelset */
		/* 
		 * POSIX 3.3.3.1
		 * The sigdelset() function deletes the individual signal
		 * specified by the value of the argument "signo" from
		 * the signal set pointed to by the argument "set".
		 */
		if (u.u_error = copyin((caddr_t)uap->arg.sigops.set, 
					      (caddr_t)&set, sizeof(sigset_t)))
			return;
		SIGM_DELSET(set, mask);
		u.u_error = copyout((caddr_t)&set, (caddr_t)uap->arg.sigops.set,
					sizeof(sigset_t));
		return;
	case SIGISMEMBER:	/* sigismember */
		/* 
		 * POSIX 3.3.3.1
		 * The sigismember() function tests whether the signal
		 * specified by the value of the argument "signo" is
		 * member of the set pointed to by the argument "set".
		 */
		if (u.u_error = copyin((caddr_t)uap->arg.sigops.set, 
					      (caddr_t)&set, sizeof(sigset_t)))
			return;
		u.u_r.r_val1 = SIGM_ISMEMBER(set, mask)?1:0;
		return;
	case SIGACTION:		/* sigaction */
		/*
		 * POSIX 3.3.4
		 *  the sigaction() function allows the calling process
		 * to examine or specify the action to be associated with
		 * a specific signal.
		 */
#ifdef DEBUG
		if (sig_debug)
			printf("ssig: SIGACTION signo=%d\n", a);
#endif
		/*
		 * if The user is requesting the old vaules
		 * then copy them.
		 */
		if (uap->arg.sigaction.oact != NULL) {
			u.u_error = copyout((caddr_t)&u.u_sa[a], (caddr_t)uap->arg.sigaction.oact,
					sizeof(siga));
			if (u.u_error) {
#ifdef DEBUG
				if (sig_debug)
					printf("ssig: SIGACTION copyout error 0x%x-->0x%x(%d)\n", &u.u_sa[a], uap->arg.sigaction.oact);
#endif
				return;
			}
		}
		/*
		 * If no action specified then return.
		 */
		if (uap->arg.sigaction.act == NULL) 
			return;
		/*
		 * Copy in the users action structure.
		 */
		if (u.u_error = copyin((caddr_t)uap->arg.sigaction.act, 
					(caddr_t)&siga, sizeof(siga)))
			return;

		/*
		 * set p_sigmask, p_sigignore and p_sigcatch masks.
		 */
		(void)p_lock(&p->p_state, SPLHI);
		u.u_sigreturn = uap->arg.sigaction.sigreturn;

		switch ((int)siga.sa_handler) {
		case (int)SIG_IGN:
			/*
			 * POSIX 3.3.4.4
			 * An attempt was made to ignore a signal the cannot be
			 * ignored
			 */
			if (SIGM_ISMEMBER(mask, CANTIGNORE)) {
				u.u_error = EINVAL;
			 	v_lock(&p->p_state, SPL0);
				return;
			}
			if (a != SIGCLD)
				SIGM_ADDSET(p->p_sigignore, mask);
			SIGM_DELSET(p->p_sigcatch, mask);
			SIGM_DELSET(p->p_sig, mask);
			break;
		case (int)SIG_DFL:
			/*
			 * POSIX IEEE 1003.1 3.3.1.3 (1) (c)
			 * Setting a signal to SIG_DFL for a signal
			 * that is pending, and whose default action is to
			 * ignore the signal, shall cause the pending
			 * signal to be discarded, wheather or not it is 
			 * blocked.
			 */
			if (SIGM_ISMEMBER(mask, DEFIGNORE)) {
				SIGM_DELSET(p->p_sig, mask);
			}
			SIGM_DELSET(p->p_sigignore, mask);
			SIGM_DELSET(p->p_sigcatch, mask);
			break;
		default:
			/*
			 * POSIX 3.3.4.4
			 * An attempt was made to catch a signal the cannot be
			 * caught
			 */
			if (SIGM_ISMEMBER(mask, CANTCATCH)) {
				u.u_error = EINVAL;
				v_lock(&p->p_state, SPL0);
				return;
			}
			SIGM_DELSET(p->p_sigignore, mask);
			SIGM_ADDSET(p->p_sigcatch, mask);
			break;
		}
		/*
		 * copy the structure.
		 */
		SIGM_DELSET(siga.sa_mask, CANTMASK);
		u.u_sa[a] = siga;

		v_lock(&p->p_state, SPL0);
		break;

	case SIGPROCMASK:	/* sigprocmask */
		/* 
		 * POSIX 3.3.5
		 * "the sigprocmask function is used to examine or change
		 * the calling process's signal mask.
		 * The vaule of the argument "how" indicates the manner in
		 * which the set is changed."
		 *
		 * "how" is encoded in uap->signo.
		 */
#ifdef DEBUG
		if (sig_debug)
			printf("ssig: SIGPROCMASK signo=%d\n", a);
#endif
		/*
		 * If the argument "oset" is not NULL, the previous mask is
		 * stored in the space pointed to by "oset".
		 */
		if ((uap->arg.sigops.oset != NULL)) {
#ifdef DEBUG
			if (sig_debug>1)
				printf("ssig: SIGPROCMASK oset@0x%x=0x%x\n", uap->arg.sigops.oset, p->p_sigmask);
#endif
			if (suword((caddr_t)uap->arg.sigops.oset, p->p_sigmask) == -1)
				u.u_error = EFAULT;
		}

		/*
		 * copy in the new set if requested.
		 */
		if ((uap->arg.sigops.set != NULL)) {
			if (u.u_error = copyin((caddr_t)uap->arg.sigops.set,
					       (caddr_t)&set, sizeof(set)) )
				return;
			/*
			 * It is not possible to block the SIGKILL and SIGSTOP
			 * signals; this shall be enforced by the system without
			 * causing an error to be indicated.
			 */
			SIGM_DELSET(set, CANTMASK);

			(void)p_lock(&p->p_state, SPLHI);
			switch (a) {
			case SIG_BLOCK:
				/*
				 * The resulting set shall be the union
				 * of the current set and the signal set 
				 * pointed to by the argument set.
				 */
				SIGM_ADDSET(p->p_sigmask , set);
#ifdef DEBUG
				if (sig_debug)
					printf("ssig: SIGPROCMASK BLOCK set=0x%x mask=0x%x\n", set, p->p_sigmask);
#endif
				break;
			case SIG_UNBLOCK:
				/*
				 * The resulting set shall be the intersection
				 * of the current set and the signal set 
				 * pointed to by the argument set.
				 */
				SIGM_DELSET(p->p_sigmask , set);
#ifdef DEBUG
				if (sig_debug)
					printf("ssig: SIGPROCMASK UNBLOCK set=0x%x mask=0x%x\n", set, p->p_sigmask);
#endif
				break;
			case SIG_SETMASK:
				/*
				 * The resulting set shall be the set
				 * pointed to by the argument set.
				 */
				p->p_sigmask = set;
#ifdef DEBUG
				if (sig_debug)
					printf("ssig: SIGPROCMASK SET set=0x%x mask=0x%x\n", set, p->p_sigmask);
#endif
				break;
			default:
				u.u_error = EINVAL;
			}
			v_lock(&p->p_state, SPL0);
		}
		return;
	case SIGSUSPEND:	/* sigsuspend */
		/*
		 * POSIX 3.3.7
		 * the sigsuspend function replaces the process's signal mask
		 * with the set of signals pointed to by the argument 
		 * "sigmask" and then suspends the process until delivery of a
		 * signal whose action is either to execute a signal-catching
		 * function or to terminate the process.
		 */
#ifdef DEBUG
		if (sig_debug)
			printf("ssig: SIGSUSPEND signo=%d\n", a);
#endif
		/*
		 * copy in the new set.
		 */
		if (u.u_error = copyin((caddr_t)uap->arg.sigops.set,
				       (caddr_t)&mask, sizeof(mask)) )
			return;
		
		SIGM_DELSET(mask, CANTMASK);
		sigpause (mask);
		/* does not return */
		return;
	case SIGPENDING:
		/*
		 * POSIX 3.3.6
		 * The sigpending() function shall store the set of signals
		 * that are blocked from delivery and pending for the calling
		 * process, in the space pointed to by the argument "set".
		 */
		mask = SIGM_INTERSECT(p->p_sig, p->p_sigmask);
		if (suword((caddr_t)uap->arg.sigops.set, mask))
			u.u_error = EFAULT;
		/*
		 * NOTE: if set_t > 32 bits then use copyout.
		 */
		return;
	default:		/* error */
		u.u_error = EINVAL;
		return;
	}
	
	if (a == SIGCHLD) {
		/*
		 * POSIX 3.3.4.2
		 * if "sig" is SIGCHLD and the SA_NOCLDSTOP flag is not
		 * set ins "sa_flags" .... then a SIGCLHD signal shall be
		 * generated for the calling process whenever any of its child
		 * processes stop.
		 *
		 * mark our state to indicate if SIGCHLD should be sent
		 * on stop
		 */
		(void)p_lock(&p->p_state, SPLHI);
		if (!(u.u_sa[SIGCLD].sa_flags & SA_NOCLDSTOP)) {
			p->p_flag |= SWSIG;
		} else {
			p->p_flag &= ~SWSIG;
		}
		v_lock(&p->p_state, SPL0);
		/*
		 * POSIX 3.3.1.3
		 * If a process establishes a signal-ctaching function for
		 * the SIGHLD signal while it has a terminated child process 
		 * for which it has not waited, it is unspecified wheather 
		 * a SIGCHLD is generated to indicate that child process.
		 *
		 * We queue SIGCLD signals to parent if children are already
		 * dead.
		 */
		for (p = u.u_procp->p_child ; p ; p = p->p_sibling) {
			if (p->p_stat == SZOMB) {
#ifdef DEBUG
				if (sig_debug)
					printf("[proc %d]ssig: got one\n");
#endif
				psignal(u.u_procp, SIGCLD);
				break;
			}
		}
	}
}


/*
 * kill system call.
 */

kill()
{
	register struct a {
		int	pid;
		int	signo;
	} *uap = (struct a *)u.u_ap;
	register proc_t *p;
	register proc_t *pp;
	int	pid;
	int	esrch;
	int	eperm;
	int	priv;
	spl_t 	s_ipl;

#ifdef DEBUG
	if (sig_debug)
		printf("kill: pid=%d(0x%x) signo=%d\n", uap->pid, uap->pid, uap->signo);
#endif
	p = u.u_procp;

	if (uap->pid < 0) {
		pid = -uap->pid;
	} else {
		pid = uap->pid;
	}

	/*
	 * The absolute value of "pid" shall not exceed {PID_MAX}.
	 * Check if send group  specified but no pgrp set.
	 */
	if ((pid > MAXPID) || ((pid == 0) && (p->p_pgrp == 0))) {
#ifdef DEBUG
		if (sig_debug)
			printf("pid %d out of range or no p_pgrp\n", pid);
#endif
		/*
		 * POSIX IEEE std 1003.1-1988 3.3.2.4
		 * ESRCH: No process or process group can be found 
		 * corresponding to that specified by pid.
		 */
		u.u_error = ESRCH;
		return;
	}

	priv = (u.u_uid == 0);

	if ((uap->signo < 0) || (uap->signo >= NSIG) || 
		(uap->signo && SIGM_ISMEMBER(CANTKILL,sigmask(uap->signo)))) {
		/*
		 * Note special case of init doing `kill(1, LEGAL_USER|nusers)' is
		 * a hook for check_sys(), telling system "/etc/init"
		 * is the *REAL* one (and passing back number of users).
		 */
#define	LEGAL_MAGIC (0x12210000)
		if ((uap->signo & 0xffff0000) == LEGAL_MAGIC) {
#ifdef DEBUG
			if (sig_debug)
				printf("ssig: LEGAL_MAGIC\n");
#endif
			if (pid == 1 && u.u_procp == &proc[1]) {
				extern unsigned sec0eaddr;
				sec0eaddr = uap->signo & 0xffff;
				good_sys();
			}
		}
		/*
		 * POSIX IEEE std 1003.1-1988 3.3.2.4
		 * EINVAL: The value of the "sig" argument is an invalid or
		 * unsupported signal number.
		 */
		u.u_error = EINVAL;
		return;
	}

	/*
	 * POSIX IEEE std 1003.1-1988 3.3.2.2
	 * If "pid" is greater than zero, "sig" is sent to all prcesses
 	 *  whoes process id is equal to "pid".
	 */
	if ((uap->pid > 0) && (pp = pfind(pid))) {
		/*
		 * pfind returns the found process with its p_state
		 * locked. If "pid" is not found, then no p_state to lock.
		 * This implementation does not find ZOMB processes.
		 */

		/*
		 * If root then send signal if specified.
		 */
		if (priv) {
#ifdef DEBUG
			if (sig_debug>1)
				printf("kill: pivilegded\n");
#endif
		    	if ((pp == &proc[1]) && (uap->signo == SIGKILL)) {
				u.u_error = EPERM;
				v_lock(&pp->p_state, SPL0);
				return;
			}
			if (uap->signo)
				lpsignal(pp, uap->signo);
			v_lock(&pp->p_state, SPL0);
			return;
		}
		/* 
		 * FIPS PUB 151  9.i - _POSIX_SAVED_IDS will be set.
		 * 
		 * POSIX IEEE std 1003.1-1988 3.3.2
		 * For a process to have permission to send a signal to
		 * a process, the real or effective user ID of the
		 * sending process, must match the real or saved set-uid
		 * user ID of the receiving process, unless the sending
		 * process has appropriate privileges.
		 */

		if ((u.u_uid == pp->p_uid) || (u.u_ruid == pp->p_uid) ||
		    (u.u_uid == pp->p_suid) || (u.u_ruid == pp->p_suid)){
			if (uap->signo)
				lpsignal(pp, uap->signo);
			v_lock(&pp->p_state, SPL0);
			return;
		}
		/*
		 * POSIX IEEE std 1003.1-1988 3.3.2.2
		 * If the implementation supports the SIGCONT signal,
		 * the user ID described above shall not be applied when
		 * sending SIGCONT to a process that is a member of the same
		 * session as the sending process.
		 */
		if ((uap->signo == SIGCONT) && (p->p_sid == pp->p_sid)) {
			lpsignal(pp, uap->signo);
			v_lock(&pp->p_state, SPL0);
			return;
		}

		/*
		 * Permission failed.
		 */
#ifdef DEBUG
		if (sig_debug)
			printf("kill: permissiion failed\n");
#endif
		v_lock(&pp->p_state, SPL0);
		/*
		 * POSIX IEEE std 1003.1-1988 3.3.2.4
		 * EPERM: The process does not have permission to send
		 * the signal to any receiving process.
		 */
		u.u_error = EPERM;
		return;
	}

	/*
	 * Lock the process list, this way we can signal all in process
	 * group and can kill runaway forks. Once proc_list is locked,
	 * a concurrent exit/wait/fork cannot happen. A race exists with
	 * p_pgrp. If a process exits before proc_list is secured, it may
	 * be waited on concurrently to the kill code. 
	 *
	 * !!!!!!!!!!!!! speed up MAXUSERS = 10000 !!!!!!!!!
	 */
	esrch = 0;	/* assume nothing found */
	eperm = 0;	/* assume no permissions */
	if (uap->pid > 0) 
		pp = &proc[1];
	else 
		pp = &proc[2];

	s_ipl = p_lock(&proc_list, SPL_PROC_LIST);

	for (; pp < v.ve_proc; pp++) {
		if (pp->p_stat == NULL)
			continue;
		/*
		 * POSIX IEEE std 1003.1-1988 3.3.2.2
		 * if "pid" is -1, the behavior of kill() is
		 * unspecified.
		 * System V
		 * if "pid" is -1, "sig" is sent to all processes.
		 */

		if ((uap->pid > 0) && (pp->p_pid != pid)) {
			continue;
		}

		(void) p_lock(&pp->p_state, SPLHI);

		/*
		 * POSIX IEEE std 1003.1-1988 3.3.2.2
		 * If "pid" is negative but not -1, "sig" shall be sent 
		 * to all processes whose group ID is equal to the
		 * absolute value of of "pid", and for which the process
		 * has permissins to send a signal.[always]
		 */
#ifdef DEBUG
		if (sig_debug>1)
			printf("kill: pid=%d pp->p_pgrp = %d\n", pid, pp->p_pgrp);
#endif
		if ((uap->pid < -1) && (pp->p_pgrp != pid)) {
			v_lock(&pp->p_state, SPL_PROC_LIST);
			continue;
		}

		/*
		 * POSIX IEEE std 1003.1-1988 3.3.2.2
		 * If "pid" is zero, "sig" is sent to all prcesses.
		 * (excluding system proceesses) whoes group id matches
		 * the group ID of the sender, and for which the process
		 * has permission to send a signal. [always]
		 */
		if (pp->p_flag&SSYS) {
			v_lock(&pp->p_state, SPL_PROC_LIST);
			continue;
		}
#ifdef DEBUG
		if (sig_debug>1)
			printf("kill: p->p_pgrp = %d\n", p->p_pgrp);
#endif
		if ((uap->pid == 0) && (pp->p_pgrp != p->p_pgrp)) {
			v_lock(&pp->p_state, SPL_PROC_LIST);
			continue;
		}

		/* 
		 * FIPS PUB 151  9.i
		 * _POSIX_SAVED_IDS is to be set
		 *
		 * POSIX IEEE std 1003.1-1988 3.3.2
		 * For a process to have permission to send a signal to
		 * a process, the real or effective user ID of the
		 * sending process, must match the real or saved set-uid
		 * user ID of the receiving process, unless the sending
		 * process has appropriate privileges.
		 */
#ifdef DEBUG
		if (sig_debug>1) {
			printf("kill:  u.u_uid:0x%x u.u_ruid:0x%x\n", u.u_uid, u.u_ruid);
			printf("kill:pp->p_uid=0x%x\n", pp->p_uid);
			printf("kill:pp->p_suid=0x%x\n", pp->p_suid);
		}
#endif
		esrch = 1;	/* mark as found (may not have permission) */
		if (priv ||
		    (u.u_uid == pp->p_uid) || (u.u_ruid == pp->p_uid)||
		    (u.u_uid == pp->p_suid) || (u.u_ruid == pp->p_suid)){
			if (uap->signo && (pp->p_stat != SZOMB))
				lpsignal(pp, uap->signo);
			eperm = 1;	/* has permission */
		}
		/*
		 * POSIX IEEE std 1003.1-1988 3.3.2.2
		 * If the implementation supports the SIGCONT signal,
		 * the user ID described above shall not be applied when
		 * sending SIGCONT to a process that is a member of the same
		 * session as the sending process.
		 */
		if ((uap->signo == SIGCONT) && (p->p_sid == pp->p_sid)) {
			if (pp->p_stat != SZOMB)
				lpsignal(pp, uap->signo);
			eperm = 1;	/* has permission */
		}
		v_lock(&pp->p_state, SPL_PROC_LIST);
	}
#ifdef DEBUG
	if (sig_debug>1)
		printf("kill: end of loop\n");
#endif
	v_lock(&proc_list, s_ipl);
	/*
	 * POSIX IEEE std 1003.1-1988 3.3.2.4
	 * ESRCH: No process or process group can be found 
	 * corresponding to that specified by pid.
	 *
	 * POSIX IEEE std 1003.1-1988 3.3.2.4
	 * EPERM: The process does not have permission to send
	 * the signal to any receiving process.
	 */
	if (esrch == 0)
		u.u_error = ESRCH;
	else if (eperm == 0)
		u.u_error = EPERM;
}

/*
 * core()
 *	Create a core image on the file "core"
 *
 * Writes UPAGES block of the user.h area followed by the entire data+stack
 * segments. The fpu context is saved in the U-area before dumping.
 *
 * Un-does any phys-mapped parts of address-space first, to avoid
 * strangeness trying to read phys-mapped stuff.
 */

core()
{
	struct	vnode	*vp;
	register size_t	dsize;
	register caddr_t vaddr;
	register int	gsiz;
	register size_t	ds;
	struct	mmap	*um;
	struct	vattr	vattr;
	static	char	corename[] = "core";

#ifdef DEBUG
	if (sig_debug)
		printf("[proc %d]core dump\n", u.u_procp-proc);
#endif
	if (u.u_uid != u.u_ruid || u.u_gid != u.u_rgid){
#ifdef DEBUG
	if (sig_debug)
		printf("core: uid mis-match\n");
#endif
		return (0);
	}

	/*
	 * Undo phys-mapped parts of address-space, replacing with
	 * invalid pages (data-seg dump below will skip these).
	 * Use while() instead of if() in case file-desc is dup'd.
	 */

	while (u.u_pmapcnt) {
#ifdef DEBUG
		if (sig_debug)
			printf("core: unmapping\n");
#endif
		for (um = u.u_mmap; um < u.u_mmapmax; um++)
			if (um->mm_pgcnt && um->mm_noio)
				munmapmm(um);
	}

	/*
	 * Arrange that traced process dump only "real" data.
	 * Note that this won't dump anything mmap'd over original text.
	 */

	dsize = u.u_dsize - u.u_tsize;
#ifdef DEBUG
	if (sig_debug>1)
		printf("core: dsize = %d - %d = %d\n", u.u_dsize, u.u_tsize, dsize);
#endif

	/*
	 * Create "core" file.
	 * Must be able writeable regular file with only one link.
	 * Don't allow if mapped, until have "i_lsize" support.
	 */

	if (ctob(UPAGES+dsize+u.u_ssize) >= dtob(u.u_limit)) {
		/* SVR3 did this */
#ifdef DEBUG
		if (sig_debug)
			printf("core: %d exceded ulimit %d\n", ctob(UPAGES+dsize+u.u_ssize), u.u_limit);
#endif
		dsize = 0;
		if (ctob(UPAGES+u.u_ssize) >= dtob(u.u_limit)) {
			u.u_ssize = 0;
			return (0);
		}
	}
	u.u_error = 0;
	vattr_null(&vattr);
	vattr.va_type = VREG;
	vattr.va_mode = 0644;
	u.u_error =
	    vn_create(corename, UIOSEG_KERNEL, &vattr, NONEXCL, VWRITE, &vp);
	if (u.u_error){
#ifdef DEBUG
		if (sig_debug)
			printf("core: cannot creat %s\n", corename);
#endif
		return (0);
	}
	if (vattr.va_nlink != 1 || vp->v_type != VREG) {
		u.u_error = EFAULT;
#ifdef DEBUG
		if (sig_debug)
			printf("core: nlink or type wrong %s\n", corename);
#endif
		goto out;
	}

	/* 
	 * If the process was an fpu user, save the fpu registers before
	 * dumping the u-area.  Should be in some machine dependent file...
	 */

	if (l.usingfpu)
		save_fpu();
#ifdef	FPA
#ifdef	EMULA_FPA
	switch (u.u_procp->p_fpa) {
	case FPA_HW:
		save_fpa();
		break;
	case FPA_NONE:
		break;
	default:
		emula_fpa_sw2hw();
		break;
	}
#else	/* !EMULA_FPA */
	if (u.u_procp->p_fpa) save_fpa();
#endif	/* EMULA_FPA */
#endif	/* FPA  */

	vattr_null(&vattr);
	vattr.va_size = 0;
	if (u.u_error = VOP_SETATTR(vp, &vattr, u.u_cred)) {
#ifdef DEBUG
		if (sig_debug)
			printf("core: bad creditials%s\n");
#endif
		goto out;
	}

	/*
	 * Write the relevant parts of the U-area: struct user and
	 * relevant stack (avoid writing whole thing since didn't
	 * necessarily zap when process was created).
	 */

	{
	int stack_offset = (caddr_t)&stack_offset - (caddr_t)VA_UBLOCK;

	u.u_error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&stack_offset,
			ctob(UPAGES) - stack_offset, stack_offset,
			UIOSEG_KERNEL, 0, (int *)0);
	}
#ifdef DEBUG
	if (u.u_error && sig_debug)
		printf("core: kernel stack write failed \n");
#endif

	/*
	 * Write process private data.  Since process can have "holes"
	 * in data-space (due to mmap/munmap/etc), arrange that any such
	 * read as zero.  Could avoid the issue and not dump beyond 1st
	 * hole, but loose some data in the core.
	 *
	 * Use vtopte() in case process swaps during writing data.
	 */

#define	COREOFF(va, sz)	(int)((int)(va) - (sz) - ctob(u.u_tsize) + ctob(UPAGES))
	vaddr = ptob(u.u_tsize);
	gsiz = 0;
	for (ds=0; ds < dsize && u.u_error == 0; ds+=CLSIZE, vaddr+=CLBYTES) {
		if (*(int*)vtopte(u.u_procp, btop(vaddr)) == PG_INVAL) {
			if (gsiz)
				u.u_error = vn_rdwr(UIO_WRITE, vp, vaddr-gsiz,
						gsiz, COREOFF(vaddr, gsiz),
						UIOSEG_USER, 0, (int *)0);
			gsiz = 0;
		} else
			gsiz += CLBYTES;
	}
	if (gsiz && u.u_error == 0)
		u.u_error = vn_rdwr(UIO_WRITE, vp, vaddr-gsiz, gsiz,
				COREOFF(vaddr, gsiz), UIOSEG_USER, 0, (int *)0);

#ifdef DEBUG
	else if (sig_debug)
		printf("core: data write failed \n");
#endif
	/*
	 * Write process private stack.
	 */

	if (u.u_error == 0)
		u.u_error = vn_rdwr(UIO_WRITE, vp,
			(caddr_t)ctob(sptov(u.u_procp, u.u_ssize - 1)),
			(int)ctob(u.u_ssize), (int)ctob(UPAGES)+(int)ctob(dsize), UIOSEG_USER,
			0, (int *)0);
#ifdef DEBUG
	else if (sig_debug)
		printf("core: stack write failed \n");
	if (sig_debug)
		printf("core: end of core \n");
#endif
out:
	VN_PUT(vp);
	return(u.u_error == 0);
}
