/*-
* Copyright (c) 1995, 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Frank van der Linden and Eric Haszlakiewicz.
*
* 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.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/
/*
* heavily from: svr4_signal.c,v 1.7 1995/01/09 01:04:21 christos Exp
*/
#ifdef DEBUG_LINUX
#define DPRINTF(a) uprintf a
#else
#define DPRINTF(a)
#endif
extern const int native_to_linux_signo[];
extern const int linux_to_native_signo[];
/*
* Convert between Linux and BSD signal sets.
*/
#if LINUX__NSIG_WORDS > 1
void
linux_old_extra_to_native_sigset(sigset_t *bss, const linux_old_sigset_t *lss, const unsigned long *extra)
{
linux_sigset_t lsnew;
/* convert old sigset to new sigset */
linux_sigemptyset(&lsnew);
lsnew.sig[0] = *lss;
if (extra)
memcpy(&lsnew.sig[1], extra,
sizeof(linux_sigset_t) - sizeof(linux_old_sigset_t));
/* convert new sigset to old sigset */
*lss = lsnew.sig[0];
if (extra)
memcpy(extra, &lsnew.sig[1],
sizeof(linux_sigset_t) - sizeof(linux_old_sigset_t));
}
#endif /* LINUX__NSIG_WORDS > 1 */
void
linux_to_native_sigset(sigset_t *bss, const linux_sigset_t *lss)
{
int i, newsig;
sigemptyset(bss);
for (i = 1; i < LINUX__NSIG; i++) {
if (linux_sigismember(lss, i)) {
newsig = linux_to_native_signo[i];
if (newsig)
sigaddset(bss, newsig);
}
}
}
void
native_to_linux_sigset(linux_sigset_t *lss, const sigset_t *bss)
{
int i, newsig;
linux_sigemptyset(lss);
for (i = 1; i < NSIG; i++) {
if (sigismember(bss, i)) {
newsig = native_to_linux_signo[i];
if (newsig)
linux_sigaddset(lss, newsig);
}
}
}
/*
* The Linux sigaction() system call. Do the usual conversions,
* and just call sigaction(). Some flags and values are silently
* ignored (see above).
*/
int
linux_sys_rt_sigaction(struct lwp *l, const struct linux_sys_rt_sigaction_args *uap, register_t *retval)
{
/* {
syscallarg(int) signum;
syscallarg(const struct linux_sigaction *) nsa;
syscallarg(struct linux_sigaction *) osa;
syscallarg(size_t) sigsetsize;
} */
struct linux_sigaction nlsa, olsa;
struct sigaction nbsa, obsa;
int error, sig;
void *tramp = NULL;
int vers = 0;
#ifdef LINUX_SA_RESTORER
struct sigacts *ps = l->l_proc->p_sigacts;
#endif
if (SCARG(uap, sigsetsize) != sizeof(linux_sigset_t))
return EINVAL;
if (SCARG(uap, nsa)) {
error = copyin(SCARG(uap, nsa), &nlsa, sizeof(nlsa));
if (error)
return error;
linux_to_native_sigaction(&nbsa, &nlsa);
}
sig = SCARG(uap, signum);
/*
* XXX: Linux has 33 realtime signals, the go binary wants to
* reset all of them; nothing else uses the last RT signal, so for
* now ignore it.
*/
if (sig == LINUX__NSIG) {
uprintf("%s: setting signal %d ignored\n", __func__, sig);
sig--; /* back to 63 which is ignored */
}
if (sig < 0 || sig >= LINUX__NSIG)
return EINVAL;
if (sig > 0 && !linux_to_native_signo[sig]) {
/* Pretend that we did something useful for unknown signals. */
obsa.sa_handler = SIG_IGN;
sigemptyset(&obsa.sa_mask);
obsa.sa_flags = 0;
} else {
#ifdef LINUX_SA_RESTORER
if (SCARG(uap, nsa) &&
(nlsa.linux_sa_flags & LINUX_SA_RESTORER) &&
(tramp = nlsa.linux_sa_restorer) != NULL)
vers = 2;
#endif
#if !defined(__aarch64__)
int
linux_sigprocmask1(struct lwp *l, int how, const linux_old_sigset_t *set, linux_old_sigset_t *oset)
{
struct proc *p = l->l_proc;
linux_old_sigset_t nlss, olss;
sigset_t nbss, obss;
int error;
switch (how) {
case LINUX_SIG_BLOCK:
how = SIG_BLOCK;
break;
case LINUX_SIG_UNBLOCK:
how = SIG_UNBLOCK;
break;
case LINUX_SIG_SETMASK:
how = SIG_SETMASK;
break;
default:
return EINVAL;
}
if (set) {
error = copyin(set, &nlss, sizeof(nlss));
if (error)
return error;
linux_old_to_native_sigset(&nbss, &nlss);
}
mutex_enter(p->p_lock);
error = sigprocmask1(l, how,
set ? &nbss : NULL, oset ? &obss : NULL);
mutex_exit(p->p_lock);
if (error)
return error;
if (oset) {
native_to_linux_old_sigset(&olss, &obss);
error = copyout(&olss, oset, sizeof(olss));
if (error)
return error;
}
return error;
}
#endif
if (SCARG(uap, sigsetsize) != sizeof(linux_sigset_t))
return EINVAL;
switch (SCARG(uap, how)) {
case LINUX_SIG_BLOCK:
how = SIG_BLOCK;
break;
case LINUX_SIG_UNBLOCK:
how = SIG_UNBLOCK;
break;
case LINUX_SIG_SETMASK:
how = SIG_SETMASK;
break;
default:
return EINVAL;
}
#if !defined(__aarch64__)
/*
* Once more: only a signal conversion is needed.
* Note: also used as sys_rt_queueinfo. The info field is ignored.
*/
int
linux_sys_rt_queueinfo(struct lwp *l, const struct linux_sys_rt_queueinfo_args *uap, register_t *retval)
{
/*
syscallarg(int) pid;
syscallarg(int) signum;
syscallarg(linix_siginfo_t *) uinfo;
*/
int error;
linux_siginfo_t info;
error = copyin(SCARG(uap, uinfo), &info, sizeof(info));
if (error)
return error;
if (info.lsi_code >= 0)
return EPERM;
/* XXX To really implement this we need to */
/* XXX keep a list of queued signals somewhere. */
return linux_sys_kill(l, (const void *)uap, retval);
}
#endif
int
native_to_linux_si_code(int code)
{
int si_codes[] = {
LINUX_SI_USER, LINUX_SI_QUEUE, LINUX_SI_TIMER, LINUX_SI_ASYNCIO,
LINUX_SI_MESGQ, LINUX_SI_TKILL /* SI_LWP */
};
if (code <= 0 && -code < __arraycount(si_codes))
return si_codes[-code];
return code;
}
int
native_to_linux_si_status(int code, int status)
{
int sts;
switch (code) {
case CLD_CONTINUED:
sts = LINUX_SIGCONT;
break;
case CLD_EXITED:
sts = status;
break;
case CLD_STOPPED:
case CLD_TRAPPED:
case CLD_DUMPED:
case CLD_KILLED:
default:
sts = native_to_linux_signo[WTERMSIG(status)];
break;
}