/*-
 * Copyright (c) 1994 Sean Eric Fagan
 * Copyright (c) 1994 Søren Schmidt
 * All rights reserved.
 *
 * Copyright (c) 1982, 1986, 1989, 1991 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer
 *    in this position and unchanged.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: stable/2.1/sys/i386/ibcs2/ibcs2_signal.c 50950 1999-09-05 08:39:22Z peter $
 */

#include <i386/ibcs2/ibcs2.h>
#include <sys/ioctl.h>
#include <sys/ioctl_compat.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/namei.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/resource.h>
#include <sys/resourcevar.h>
#include <sys/signal.h>
#include <sys/signalvar.h>
#include <sys/stat.h>
#include <sys/tty.h>
#include <sys/termios.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/vnode.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/vm_map.h>
#include <vm/vm_kern.h>


#define DONTMASK	(sigmask(SIGKILL)|sigmask(SIGSTOP)|sigmask(SIGCHLD))

int bsd_to_ibcs2_signal[NSIG] = {
  	0, IBCS2_SIGHUP, IBCS2_SIGINT, IBCS2_SIGQUIT,
  	IBCS2_SIGILL, IBCS2_SIGTRAP, IBCS2_SIGABRT, IBCS2_SIGEMT,
  	IBCS2_SIGFPE, IBCS2_SIGKILL, IBCS2_SIGBUS, IBCS2_SIGSEGV,
  	IBCS2_SIGSYS, IBCS2_SIGPIPE, IBCS2_SIGALRM, IBCS2_SIGTERM,
  	IBCS2_SIGURG, IBCS2_SIGSTOP, IBCS2_SIGTSTP, IBCS2_SIGCONT,
  	IBCS2_SIGCHLD, IBCS2_SIGTTIN, IBCS2_SIGTTOU, IBCS2_SIGIO,
  	IBCS2_SIGGXCPU, IBCS2_SIGGXFSZ, IBCS2_SIGVTALRM, IBCS2_SIGPROF,
  	IBCS2_SIGWINCH, 0, IBCS2_SIGUSR1, IBCS2_SIGUSR2
};

int ibcs2_to_bsd_signal[IBCS2_NSIG] = {
  	0, SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGEMT,
  	SIGFPE, SIGKILL, SIGBUS, SIGSEGV, SIGSYS, SIGPIPE, SIGALRM, SIGTERM,
  	SIGUSR1, SIGUSR2, SIGCHLD, 0, SIGWINCH, SIGURG, SIGIO, SIGSTOP,
  	SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU, SIGVTALRM, SIGPROF, SIGXCPU, SIGXFSZ
};

static char ibcs2_sig_name[IBCS2_NSIG][10] = {
  	"UNKNOWN", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP",
	"SIGABRT", "SIGEMT", "SIGFPE", "SIGKILL", "SIGBUS", "SIGSEGV",
	"SIGSYS", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGUSR1", "SIGUSR2",
	"SIGCHLD", "SIGPWR", "SIGWINCH", "SIGURG", "SIGIO", "SIGSTOP",
	"SIGTSTP", "SIGCONT", "SIGTTIN", "SIGTTOU", "SIGVTALRM",
	"SIGPROF", "SIGXCPU", "SIGXFSZ"
};

/*#define LEGAL_SIG(x)	\
	(((x & IBCS2_SIGMASK) < IBCS2_NSIG) ? (x & IBCS2_SIGMASK) : (0))*/
int
LEGAL_SIG(int sig)
{
	if ((sig & IBCS2_SIGMASK) > IBCS2_NSIG) {
		printf("IBCS2: illegal ibcs2 signal %d(%08x)\n",
			sig & IBCS2_SIGMASK, sig);
		return 0;
	}
	else
		return (sig & IBCS2_SIGMASK);
}

char *
ibcs2_sig_to_str(int sig)
{
	if (sig > IBCS2_NSIG) {
		printf("IBCS2: ibcs2 signal out of range (%d)\n", sig);
		return ibcs2_sig_name[0];
	}
	else
		return ibcs2_sig_name[sig];
}

static sig_t
ibcs2_to_bsd_sigfunc(ibcs2_sig_t func) {
	switch ((int)func) {
	case IBCS2_SIG_DFL:
		return SIG_DFL;
	case IBCS2_SIG_IGN:
		return SIG_IGN;
	case IBCS2_SIG_HOLD:
		return SIG_HOLD;
	default:
		return func;
	}
}

static ibcs2_sig_t
bsd_to_ibcs2_sigfunc(sig_t func) {
	switch ((int)func) {
	case SIG_DFL:
		return IBCS2_SIG_DFL;
	case SIG_IGN:
		return IBCS2_SIG_IGN;
	case SIG_CATCH:
		printf("IBCS2: Oops - SIG_CATCH does not translate :-(\n");
		return IBCS2_SIG_DFL;
	case SIG_HOLD:
		return IBCS2_SIG_HOLD;
	default:
		return func;
	}
}

static sigset_t
ibcs2_to_bsd_sigmask(ibcs2_sigset_t mask) {
  	int i;
  	sigset_t new = 0;

  	for (i = 1; i < NSIG; i++)
    		if (mask & (1 << i-1))
      			new |= (1 << (ibcs2_to_bsd_signal[i]-1));
  	return new;
}

static ibcs2_sigset_t
bsd_to_ibcs2_sigmask(sigset_t mask) {
  	int i;
  	sigset_t new = 0;

  	for (i = 1; i < IBCS2_NSIG; i++)
    		if (mask & (1 << i-1))
      			new |= (1 << (bsd_to_ibcs2_signal[i]-1));
  	return new;
}

struct ibcs2_signal_args {
	int signo;
	ibcs2_sig_t func;
};

static int
ibcs2_sigset(struct proc *p, struct ibcs2_signal_args *args, int *retval)
{
	struct sigaction tmp;
	int sig_bsd = ibcs2_to_bsd_signal[LEGAL_SIG(args->signo)];

    	*retval = (int)bsd_to_ibcs2_sigfunc(p->p_sigacts->ps_sigact[sig_bsd]);
	if (args->func == IBCS2_SIG_HOLD) {
    		(void) splhigh();
    		p->p_sigmask |= (sigmask(sig_bsd) &~ DONTMASK);
    		(void) spl0();
	}
	else {
		tmp.sa_mask = sigmask(sig_bsd);
    		tmp.sa_handler = ibcs2_to_bsd_sigfunc(args->func);
  		tmp.sa_flags = 0;
   		setsigvec(p, sig_bsd, &tmp);
	}
    	return 0;
}

static int
ibcs2_sighold(struct proc *p, struct ibcs2_signal_args *args, int *retval)
{
	int sig_bsd = ibcs2_to_bsd_signal[LEGAL_SIG(args->signo)];

	(void) splhigh();
    	*retval = p->p_sigmask;
    	p->p_sigmask |= (sigmask(sig_bsd) & ~DONTMASK);
    	(void) spl0();
    	return 0;
}

static int
ibcs2_sigrelse(struct proc *p, struct ibcs2_signal_args *args, int *retval)
{
	int sig_bsd = ibcs2_to_bsd_signal[LEGAL_SIG(args->signo)];

	(void) splhigh();
    	*retval = p->p_sigmask;
    	p->p_sigmask &= ~sigmask(sig_bsd);
    	(void) spl0();
    	return 0;
}

static int
ibcs2_sigignore(struct proc *p, struct ibcs2_signal_args *args, int *retval)
{
	struct sigaction tmp;
	int sig_bsd = ibcs2_to_bsd_signal[LEGAL_SIG(args->signo)];

	tmp.sa_mask = sigmask(sig_bsd);
    	tmp.sa_handler = SIG_IGN;
    	tmp.sa_flags = 0;
    	*retval = (int)bsd_to_ibcs2_sigfunc(p->p_sigacts->ps_sigact[sig_bsd]);
	setsigvec(p, sig_bsd, &tmp);
    	return 0;
}

static int
ibcs2_sigpause(struct proc *p, struct ibcs2_signal_args *args, int *retval)
{
	struct sigacts *ps = p->p_sigacts;
	int sig_bsd = ibcs2_to_bsd_signal[LEGAL_SIG(args->signo)];

	ps->ps_oldmask = p->p_sigmask;
	ps->ps_flags |= SAS_OLDMASK;
	p->p_sigmask = sigmask(sig_bsd) &~ DONTMASK;
	(void) tsleep((caddr_t) ps, PPAUSE|PCATCH, "i-pause", 0);
  	*retval = -1;
    	return EINTR;
}

static int
ibcs2_signal(struct proc *p, struct ibcs2_signal_args *args, int *retval)
{
	struct sigaction tmp;
	int sig_bsd = ibcs2_to_bsd_signal[LEGAL_SIG(args->signo)];

    	tmp.sa_mask = sigmask(sig_bsd);
    	tmp.sa_handler = ibcs2_to_bsd_sigfunc(args->func);
    	tmp.sa_flags = 0;
    	*retval = (int)bsd_to_ibcs2_sigfunc(p->p_sigacts->ps_sigact[sig_bsd]);
	setsigvec(p, sig_bsd, &tmp);
    	return 0;
}

int
ibcs2_sigsys(struct proc *p, struct ibcs2_signal_args *args, int *retval)
{
	if (ibcs2_trace & IBCS2_TRACE_SIGNAL)
		printf("IBCS2: 'sigsys' signo=%d(%s) ",
			args->signo & IBCS2_SIGMASK,
			ibcs2_sig_to_str(args->signo & IBCS2_SIGMASK));

	switch (args->signo & ~IBCS2_SIGMASK ) {
	case 0x0000:
		if (ibcs2_trace & IBCS2_TRACE_SIGNAL)
			printf("signal() func=%x\n", args->func);
		return ibcs2_signal(p, args, retval);
	case 0x0100:
		if (ibcs2_trace & IBCS2_TRACE_SIGNAL)
			printf("sigset() func=%x\n", args->func);
		return ibcs2_sigset(p, args, retval);
	case 0x0200:
		if (ibcs2_trace & IBCS2_TRACE_SIGNAL)
			printf("sighold()\n");
		return ibcs2_sighold(p, args, retval);
	case 0x0400:
		if (ibcs2_trace & IBCS2_TRACE_SIGNAL)
			printf("sigrelse()\n");
		return ibcs2_sigrelse(p, args, retval);
	case 0x0800:
		if (ibcs2_trace & IBCS2_TRACE_SIGNAL)
			printf("sigignore()\n");
		return ibcs2_sigignore(p, args, retval);
	case 0x1000:
		if (ibcs2_trace & IBCS2_TRACE_SIGNAL)
			printf("sigpause()\n");
		return ibcs2_sigpause(p, args, retval);
	default:
		printf("IBCS2: unknown signal action\n"); break;
	}
	*retval = -1;
  	return EINVAL;
}

struct ibcs2_sigaction_args {
  	int signo;
  	struct sigaction *osa, *nsa;
};

int
ibcs2_sigaction(struct proc *p, struct ibcs2_sigaction_args *args, int *retval)
{
	struct sigaction vec;
	register struct sigaction *sa;
	register struct sigacts *ps = p->p_sigacts;
	register int sig;
	int bit, error;

	if (ibcs2_trace & IBCS2_TRACE_SIGNAL)
		printf("IBCS2: 'sigaction' signo=%d(%s)\n",
			args->signo, ibcs2_sig_to_str(args->signo));
	sig = ibcs2_to_bsd_signal[LEGAL_SIG(args->signo)];
	if (sig <= 0 || sig >= NSIG || sig == SIGKILL || sig == SIGSTOP) {
		*retval = -1;
		return EINVAL;
	}
	sa = &vec;
	if (args->osa) {
		sa->sa_handler = ps->ps_sigact[sig];
		sa->sa_mask = ps->ps_catchmask[sig];
		bit = sigmask(sig);
		sa->sa_flags = 0;
		if (p->p_flag & SA_NOCLDSTOP)
			sa->sa_flags = IBCS2_SA_NOCLDSTOP;
		if (error = copyout((caddr_t)sa, (caddr_t)args->osa,
		    sizeof(vec))) {
			*retval = -1;
			return error;
		}
	}
	if (args->nsa) {
		if (error = copyin((caddr_t)args->nsa, (caddr_t)sa,
		    sizeof(vec))) {
			*retval = -1;
			return error;
		}
		/*
		 * iBCS2 only defines one SA_ flag right now
		 */
		if (vec.sa_flags & IBCS2_SA_NOCLDSTOP)
		  vec.sa_flags = SA_NOCLDSTOP;
		setsigvec(p, sig, sa);
	}
	*retval = 0;
	return 0;
}

struct ibcs2_sigprocmask_args {
  	int how;
  	unsigned long *mask;
  	unsigned long *omask;
};

int
ibcs2_sigprocmask(struct proc *p, struct ibcs2_sigprocmask_args *args, int *retval)
{
	int error;
	sigset_t umask;
	sigset_t omask = bsd_to_ibcs2_sigmask(p->p_sigmask);

	if (ibcs2_trace & IBCS2_TRACE_SIGNAL)
		printf("IBCS2: 'sigprocmask' how=%d\n", args->how);
	if (error = copyin(args->mask, &umask, sizeof(args->mask))) {
		*retval = -1;
	  	return error;
	}
	umask = ibcs2_to_bsd_sigmask(umask);
	if (args->omask)
	  	if (error = copyout(&omask, args->omask, sizeof(args->omask))) {
			*retval = -1;
	    		return error;
		}
	(void) splhigh();
	switch (args->how) {
	case 0:	/* SIG_SETMASK */
		p->p_sigmask = umask &~ DONTMASK;
		break;

	case 1:	/* SIG_BLOCK */
		p->p_sigmask |= (umask &~ DONTMASK);
		break;

	case 2:	/* SIG_UNBLOCK */
		p->p_sigmask &= ~umask;
		break;

	default:
		error = EINVAL;
		break;
	}
	(void) spl0();
	if (error)
		*retval = -1;
	else
		*retval = 0;
 	return error;
}

struct ibcs2_sigpending_args {
  	unsigned long *sigs;
};

int
ibcs2_sigpending(struct proc *p, struct ibcs2_sigpending_args *args, int *retval)
{
  	int error;
  	sigset_t mask = bsd_to_ibcs2_sigmask(p->p_siglist);

	if (ibcs2_trace & IBCS2_TRACE_SIGNAL)
		printf("IBCS2: 'sigpending' which=%x\n", args->sigs);
  	if (error = copyout(&mask, args->sigs, sizeof(unsigned long)))
  		*retval = -1;
	else
		*retval = 0;
    	return error;
}

struct ibcs2_sigsuspend_args {
  	sigset_t *mask;
};

int
ibcs2_sigsuspend(struct proc *p, struct ibcs2_sigsuspend_args *args, int *retval)
{
  	sigset_t mask = ibcs2_to_bsd_sigmask((sigset_t)args->mask);

	if (ibcs2_trace & IBCS2_TRACE_SIGNAL)
		printf("IBCS2: 'sigsuspend'\n");
	return sigsuspend(p, &mask, retval);
}

struct kill_args {
  	int pid;
  	int signo;
};

int
ibcs2_kill(struct proc *p, struct kill_args *args, int *retval)
{
  	struct kill_args tmp;

	if (ibcs2_trace & IBCS2_TRACE_SIGNAL)
		printf("IBCS2: 'kill' pid=%d, sig=%d(%s)\n", args->pid,
			args->signo, ibcs2_sig_to_str(args->signo));
  	tmp.pid = args->pid;
  	tmp.signo = (args->signo < IBCS2_NSIG) ?
			ibcs2_to_bsd_signal[args->signo] : IBCS2_NSIG;
	return kill(p, &tmp, retval);
}

