fc55d94b46
1. Linux sigset always 64 bit on all platforms. In order to move Linux sigset code to the linux_common module define it as 64 bit int. Move Linux sigset manipulation routines to the MI path. 2. Move Linux signal number definitions to the MI path. In general, they are the same on all platforms except for a few signals. 3. Map Linux RT signals to the FreeBSD RT signals and hide signal conversion tables to avoid conversion errors. 4. Emulate Linux SIGPWR signal via FreeBSD SIGRTMIN signal which is outside of allowed on Linux signal numbers. PR: 197216
767 lines
17 KiB
C
767 lines
17 KiB
C
/*-
|
|
* Copyright (c) 1994-1995 Søren Schmidt
|
|
* 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. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/sx.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/signalvar.h>
|
|
#include <sys/syscallsubr.h>
|
|
#include <sys/sysproto.h>
|
|
|
|
#include <security/audit/audit.h>
|
|
|
|
#include "opt_compat.h"
|
|
|
|
#ifdef COMPAT_LINUX32
|
|
#include <machine/../linux32/linux.h>
|
|
#include <machine/../linux32/linux32_proto.h>
|
|
#else
|
|
#include <machine/../linux/linux.h>
|
|
#include <machine/../linux/linux_proto.h>
|
|
#endif
|
|
#include <compat/linux/linux_signal.h>
|
|
#include <compat/linux/linux_util.h>
|
|
#include <compat/linux/linux_emul.h>
|
|
#include <compat/linux/linux_misc.h>
|
|
|
|
static int linux_do_tkill(struct thread *td, struct thread *tdt,
|
|
ksiginfo_t *ksi);
|
|
static void sicode_to_lsicode(int si_code, int *lsi_code);
|
|
|
|
|
|
static void
|
|
linux_to_bsd_sigaction(l_sigaction_t *lsa, struct sigaction *bsa)
|
|
{
|
|
|
|
linux_to_bsd_sigset(&lsa->lsa_mask, &bsa->sa_mask);
|
|
bsa->sa_handler = PTRIN(lsa->lsa_handler);
|
|
bsa->sa_flags = 0;
|
|
if (lsa->lsa_flags & LINUX_SA_NOCLDSTOP)
|
|
bsa->sa_flags |= SA_NOCLDSTOP;
|
|
if (lsa->lsa_flags & LINUX_SA_NOCLDWAIT)
|
|
bsa->sa_flags |= SA_NOCLDWAIT;
|
|
if (lsa->lsa_flags & LINUX_SA_SIGINFO)
|
|
bsa->sa_flags |= SA_SIGINFO;
|
|
if (lsa->lsa_flags & LINUX_SA_ONSTACK)
|
|
bsa->sa_flags |= SA_ONSTACK;
|
|
if (lsa->lsa_flags & LINUX_SA_RESTART)
|
|
bsa->sa_flags |= SA_RESTART;
|
|
if (lsa->lsa_flags & LINUX_SA_ONESHOT)
|
|
bsa->sa_flags |= SA_RESETHAND;
|
|
if (lsa->lsa_flags & LINUX_SA_NOMASK)
|
|
bsa->sa_flags |= SA_NODEFER;
|
|
}
|
|
|
|
static void
|
|
bsd_to_linux_sigaction(struct sigaction *bsa, l_sigaction_t *lsa)
|
|
{
|
|
|
|
bsd_to_linux_sigset(&bsa->sa_mask, &lsa->lsa_mask);
|
|
#ifdef COMPAT_LINUX32
|
|
lsa->lsa_handler = (uintptr_t)bsa->sa_handler;
|
|
#else
|
|
lsa->lsa_handler = bsa->sa_handler;
|
|
#endif
|
|
lsa->lsa_restorer = 0; /* unsupported */
|
|
lsa->lsa_flags = 0;
|
|
if (bsa->sa_flags & SA_NOCLDSTOP)
|
|
lsa->lsa_flags |= LINUX_SA_NOCLDSTOP;
|
|
if (bsa->sa_flags & SA_NOCLDWAIT)
|
|
lsa->lsa_flags |= LINUX_SA_NOCLDWAIT;
|
|
if (bsa->sa_flags & SA_SIGINFO)
|
|
lsa->lsa_flags |= LINUX_SA_SIGINFO;
|
|
if (bsa->sa_flags & SA_ONSTACK)
|
|
lsa->lsa_flags |= LINUX_SA_ONSTACK;
|
|
if (bsa->sa_flags & SA_RESTART)
|
|
lsa->lsa_flags |= LINUX_SA_RESTART;
|
|
if (bsa->sa_flags & SA_RESETHAND)
|
|
lsa->lsa_flags |= LINUX_SA_ONESHOT;
|
|
if (bsa->sa_flags & SA_NODEFER)
|
|
lsa->lsa_flags |= LINUX_SA_NOMASK;
|
|
}
|
|
|
|
int
|
|
linux_do_sigaction(struct thread *td, int linux_sig, l_sigaction_t *linux_nsa,
|
|
l_sigaction_t *linux_osa)
|
|
{
|
|
struct sigaction act, oact, *nsa, *osa;
|
|
int error, sig;
|
|
|
|
if (!LINUX_SIG_VALID(linux_sig))
|
|
return (EINVAL);
|
|
|
|
osa = (linux_osa != NULL) ? &oact : NULL;
|
|
if (linux_nsa != NULL) {
|
|
nsa = &act;
|
|
linux_to_bsd_sigaction(linux_nsa, nsa);
|
|
} else
|
|
nsa = NULL;
|
|
sig = linux_to_bsd_signal(linux_sig);
|
|
|
|
error = kern_sigaction(td, sig, nsa, osa, 0);
|
|
if (error)
|
|
return (error);
|
|
|
|
if (linux_osa != NULL)
|
|
bsd_to_linux_sigaction(osa, linux_osa);
|
|
|
|
return (0);
|
|
}
|
|
|
|
#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
|
|
int
|
|
linux_signal(struct thread *td, struct linux_signal_args *args)
|
|
{
|
|
l_sigaction_t nsa, osa;
|
|
int error;
|
|
|
|
#ifdef DEBUG
|
|
if (ldebug(signal))
|
|
printf(ARGS(signal, "%d, %p"),
|
|
args->sig, (void *)(uintptr_t)args->handler);
|
|
#endif
|
|
|
|
nsa.lsa_handler = args->handler;
|
|
nsa.lsa_flags = LINUX_SA_ONESHOT | LINUX_SA_NOMASK;
|
|
LINUX_SIGEMPTYSET(nsa.lsa_mask);
|
|
|
|
error = linux_do_sigaction(td, args->sig, &nsa, &osa);
|
|
td->td_retval[0] = (int)(intptr_t)osa.lsa_handler;
|
|
|
|
return (error);
|
|
}
|
|
#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
|
|
|
|
int
|
|
linux_rt_sigaction(struct thread *td, struct linux_rt_sigaction_args *args)
|
|
{
|
|
l_sigaction_t nsa, osa;
|
|
int error;
|
|
|
|
#ifdef DEBUG
|
|
if (ldebug(rt_sigaction))
|
|
printf(ARGS(rt_sigaction, "%ld, %p, %p, %ld"),
|
|
(long)args->sig, (void *)args->act,
|
|
(void *)args->oact, (long)args->sigsetsize);
|
|
#endif
|
|
|
|
if (args->sigsetsize != sizeof(l_sigset_t))
|
|
return (EINVAL);
|
|
|
|
if (args->act != NULL) {
|
|
error = copyin(args->act, &nsa, sizeof(l_sigaction_t));
|
|
if (error)
|
|
return (error);
|
|
}
|
|
|
|
error = linux_do_sigaction(td, args->sig,
|
|
args->act ? &nsa : NULL,
|
|
args->oact ? &osa : NULL);
|
|
|
|
if (args->oact != NULL && !error) {
|
|
error = copyout(&osa, args->oact, sizeof(l_sigaction_t));
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
linux_do_sigprocmask(struct thread *td, int how, l_sigset_t *new,
|
|
l_sigset_t *old)
|
|
{
|
|
sigset_t omask, nmask;
|
|
sigset_t *nmaskp;
|
|
int error;
|
|
|
|
td->td_retval[0] = 0;
|
|
|
|
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 (new != NULL) {
|
|
linux_to_bsd_sigset(new, &nmask);
|
|
nmaskp = &nmask;
|
|
} else
|
|
nmaskp = NULL;
|
|
error = kern_sigprocmask(td, how, nmaskp, &omask, 0);
|
|
if (error == 0 && old != NULL)
|
|
bsd_to_linux_sigset(&omask, old);
|
|
|
|
return (error);
|
|
}
|
|
|
|
#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
|
|
int
|
|
linux_sigprocmask(struct thread *td, struct linux_sigprocmask_args *args)
|
|
{
|
|
l_osigset_t mask;
|
|
l_sigset_t set, oset;
|
|
int error;
|
|
|
|
#ifdef DEBUG
|
|
if (ldebug(sigprocmask))
|
|
printf(ARGS(sigprocmask, "%d, *, *"), args->how);
|
|
#endif
|
|
|
|
if (args->mask != NULL) {
|
|
error = copyin(args->mask, &mask, sizeof(l_osigset_t));
|
|
if (error)
|
|
return (error);
|
|
LINUX_SIGEMPTYSET(set);
|
|
set.__mask = mask;
|
|
}
|
|
|
|
error = linux_do_sigprocmask(td, args->how,
|
|
args->mask ? &set : NULL,
|
|
args->omask ? &oset : NULL);
|
|
|
|
if (args->omask != NULL && !error) {
|
|
mask = oset.__mask;
|
|
error = copyout(&mask, args->omask, sizeof(l_osigset_t));
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
|
|
|
|
int
|
|
linux_rt_sigprocmask(struct thread *td, struct linux_rt_sigprocmask_args *args)
|
|
{
|
|
l_sigset_t set, oset;
|
|
int error;
|
|
|
|
#ifdef DEBUG
|
|
if (ldebug(rt_sigprocmask))
|
|
printf(ARGS(rt_sigprocmask, "%d, %p, %p, %ld"),
|
|
args->how, (void *)args->mask,
|
|
(void *)args->omask, (long)args->sigsetsize);
|
|
#endif
|
|
|
|
if (args->sigsetsize != sizeof(l_sigset_t))
|
|
return EINVAL;
|
|
|
|
if (args->mask != NULL) {
|
|
error = copyin(args->mask, &set, sizeof(l_sigset_t));
|
|
if (error)
|
|
return (error);
|
|
}
|
|
|
|
error = linux_do_sigprocmask(td, args->how,
|
|
args->mask ? &set : NULL,
|
|
args->omask ? &oset : NULL);
|
|
|
|
if (args->omask != NULL && !error) {
|
|
error = copyout(&oset, args->omask, sizeof(l_sigset_t));
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
|
|
int
|
|
linux_sgetmask(struct thread *td, struct linux_sgetmask_args *args)
|
|
{
|
|
struct proc *p = td->td_proc;
|
|
l_sigset_t mask;
|
|
|
|
#ifdef DEBUG
|
|
if (ldebug(sgetmask))
|
|
printf(ARGS(sgetmask, ""));
|
|
#endif
|
|
|
|
PROC_LOCK(p);
|
|
bsd_to_linux_sigset(&td->td_sigmask, &mask);
|
|
PROC_UNLOCK(p);
|
|
td->td_retval[0] = mask.__mask;
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
linux_ssetmask(struct thread *td, struct linux_ssetmask_args *args)
|
|
{
|
|
struct proc *p = td->td_proc;
|
|
l_sigset_t lset;
|
|
sigset_t bset;
|
|
|
|
#ifdef DEBUG
|
|
if (ldebug(ssetmask))
|
|
printf(ARGS(ssetmask, "%08lx"), (unsigned long)args->mask);
|
|
#endif
|
|
|
|
PROC_LOCK(p);
|
|
bsd_to_linux_sigset(&td->td_sigmask, &lset);
|
|
td->td_retval[0] = lset.__mask;
|
|
LINUX_SIGEMPTYSET(lset);
|
|
lset.__mask = args->mask;
|
|
linux_to_bsd_sigset(&lset, &bset);
|
|
td->td_sigmask = bset;
|
|
SIG_CANTMASK(td->td_sigmask);
|
|
signotify(td);
|
|
PROC_UNLOCK(p);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
linux_sigpending(struct thread *td, struct linux_sigpending_args *args)
|
|
{
|
|
struct proc *p = td->td_proc;
|
|
sigset_t bset;
|
|
l_sigset_t lset;
|
|
l_osigset_t mask;
|
|
|
|
#ifdef DEBUG
|
|
if (ldebug(sigpending))
|
|
printf(ARGS(sigpending, "*"));
|
|
#endif
|
|
|
|
PROC_LOCK(p);
|
|
bset = p->p_siglist;
|
|
SIGSETOR(bset, td->td_siglist);
|
|
SIGSETAND(bset, td->td_sigmask);
|
|
PROC_UNLOCK(p);
|
|
bsd_to_linux_sigset(&bset, &lset);
|
|
mask = lset.__mask;
|
|
return (copyout(&mask, args->mask, sizeof(mask)));
|
|
}
|
|
#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
|
|
|
|
/*
|
|
* MPSAFE
|
|
*/
|
|
int
|
|
linux_rt_sigpending(struct thread *td, struct linux_rt_sigpending_args *args)
|
|
{
|
|
struct proc *p = td->td_proc;
|
|
sigset_t bset;
|
|
l_sigset_t lset;
|
|
|
|
if (args->sigsetsize > sizeof(lset))
|
|
return EINVAL;
|
|
/* NOT REACHED */
|
|
|
|
#ifdef DEBUG
|
|
if (ldebug(rt_sigpending))
|
|
printf(ARGS(rt_sigpending, "*"));
|
|
#endif
|
|
|
|
PROC_LOCK(p);
|
|
bset = p->p_siglist;
|
|
SIGSETOR(bset, td->td_siglist);
|
|
SIGSETAND(bset, td->td_sigmask);
|
|
PROC_UNLOCK(p);
|
|
bsd_to_linux_sigset(&bset, &lset);
|
|
return (copyout(&lset, args->set, args->sigsetsize));
|
|
}
|
|
|
|
/*
|
|
* MPSAFE
|
|
*/
|
|
int
|
|
linux_rt_sigtimedwait(struct thread *td,
|
|
struct linux_rt_sigtimedwait_args *args)
|
|
{
|
|
int error, sig;
|
|
l_timeval ltv;
|
|
struct timeval tv;
|
|
struct timespec ts, *tsa;
|
|
l_sigset_t lset;
|
|
sigset_t bset;
|
|
l_siginfo_t linfo;
|
|
ksiginfo_t info;
|
|
|
|
#ifdef DEBUG
|
|
if (ldebug(rt_sigtimedwait))
|
|
printf(ARGS(rt_sigtimedwait, "*"));
|
|
#endif
|
|
if (args->sigsetsize != sizeof(l_sigset_t))
|
|
return (EINVAL);
|
|
|
|
if ((error = copyin(args->mask, &lset, sizeof(lset))))
|
|
return (error);
|
|
linux_to_bsd_sigset(&lset, &bset);
|
|
|
|
tsa = NULL;
|
|
if (args->timeout) {
|
|
if ((error = copyin(args->timeout, <v, sizeof(ltv))))
|
|
return (error);
|
|
#ifdef DEBUG
|
|
if (ldebug(rt_sigtimedwait))
|
|
printf(LMSG("linux_rt_sigtimedwait: "
|
|
"incoming timeout (%jd/%jd)\n"),
|
|
(intmax_t)ltv.tv_sec, (intmax_t)ltv.tv_usec);
|
|
#endif
|
|
tv.tv_sec = (long)ltv.tv_sec;
|
|
tv.tv_usec = (suseconds_t)ltv.tv_usec;
|
|
if (itimerfix(&tv)) {
|
|
/*
|
|
* The timeout was invalid. Convert it to something
|
|
* valid that will act as it does under Linux.
|
|
*/
|
|
tv.tv_sec += tv.tv_usec / 1000000;
|
|
tv.tv_usec %= 1000000;
|
|
if (tv.tv_usec < 0) {
|
|
tv.tv_sec -= 1;
|
|
tv.tv_usec += 1000000;
|
|
}
|
|
if (tv.tv_sec < 0)
|
|
timevalclear(&tv);
|
|
#ifdef DEBUG
|
|
if (ldebug(rt_sigtimedwait))
|
|
printf(LMSG("linux_rt_sigtimedwait: "
|
|
"converted timeout (%jd/%ld)\n"),
|
|
(intmax_t)tv.tv_sec, tv.tv_usec);
|
|
#endif
|
|
}
|
|
TIMEVAL_TO_TIMESPEC(&tv, &ts);
|
|
tsa = &ts;
|
|
}
|
|
error = kern_sigtimedwait(td, bset, &info, tsa);
|
|
#ifdef DEBUG
|
|
if (ldebug(rt_sigtimedwait))
|
|
printf(LMSG("linux_rt_sigtimedwait: "
|
|
"sigtimedwait returning (%d)\n"), error);
|
|
#endif
|
|
if (error)
|
|
return (error);
|
|
|
|
sig = bsd_to_linux_signal(info.ksi_signo);
|
|
|
|
if (args->ptr) {
|
|
memset(&linfo, 0, sizeof(linfo));
|
|
ksiginfo_to_lsiginfo(&info, &linfo, sig);
|
|
error = copyout(&linfo, args->ptr, sizeof(linfo));
|
|
}
|
|
if (error == 0)
|
|
td->td_retval[0] = sig;
|
|
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
linux_kill(struct thread *td, struct linux_kill_args *args)
|
|
{
|
|
struct kill_args /* {
|
|
int pid;
|
|
int signum;
|
|
} */ tmp;
|
|
|
|
#ifdef DEBUG
|
|
if (ldebug(kill))
|
|
printf(ARGS(kill, "%d, %d"), args->pid, args->signum);
|
|
#endif
|
|
|
|
/*
|
|
* Allow signal 0 as a means to check for privileges
|
|
*/
|
|
if (!LINUX_SIG_VALID(args->signum) && args->signum != 0)
|
|
return (EINVAL);
|
|
|
|
if (args->signum > 0)
|
|
tmp.signum = linux_to_bsd_signal(args->signum);
|
|
else
|
|
tmp.signum = 0;
|
|
|
|
tmp.pid = args->pid;
|
|
return (sys_kill(td, &tmp));
|
|
}
|
|
|
|
static int
|
|
linux_do_tkill(struct thread *td, struct thread *tdt, ksiginfo_t *ksi)
|
|
{
|
|
struct proc *p;
|
|
int error;
|
|
|
|
p = tdt->td_proc;
|
|
AUDIT_ARG_SIGNUM(ksi->ksi_signo);
|
|
AUDIT_ARG_PID(p->p_pid);
|
|
AUDIT_ARG_PROCESS(p);
|
|
|
|
error = p_cansignal(td, p, ksi->ksi_signo);
|
|
if (error != 0 || ksi->ksi_signo == 0)
|
|
goto out;
|
|
|
|
tdksignal(tdt, ksi->ksi_signo, ksi);
|
|
|
|
out:
|
|
PROC_UNLOCK(p);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
linux_tgkill(struct thread *td, struct linux_tgkill_args *args)
|
|
{
|
|
struct thread *tdt;
|
|
ksiginfo_t ksi;
|
|
int sig;
|
|
|
|
#ifdef DEBUG
|
|
if (ldebug(tgkill))
|
|
printf(ARGS(tgkill, "%d, %d, %d"),
|
|
args->tgid, args->pid, args->sig);
|
|
#endif
|
|
|
|
if (args->pid <= 0 || args->tgid <=0)
|
|
return (EINVAL);
|
|
|
|
/*
|
|
* Allow signal 0 as a means to check for privileges
|
|
*/
|
|
if (!LINUX_SIG_VALID(args->sig) && args->sig != 0)
|
|
return (EINVAL);
|
|
|
|
if (args->sig > 0)
|
|
sig = linux_to_bsd_signal(args->sig);
|
|
else
|
|
sig = 0;
|
|
|
|
tdt = linux_tdfind(td, args->pid, args->tgid);
|
|
if (tdt == NULL)
|
|
return (ESRCH);
|
|
|
|
ksiginfo_init(&ksi);
|
|
ksi.ksi_signo = sig;
|
|
ksi.ksi_code = SI_LWP;
|
|
ksi.ksi_errno = 0;
|
|
ksi.ksi_pid = td->td_proc->p_pid;
|
|
ksi.ksi_uid = td->td_proc->p_ucred->cr_ruid;
|
|
return (linux_do_tkill(td, tdt, &ksi));
|
|
}
|
|
|
|
/*
|
|
* Deprecated since 2.5.75. Replaced by tgkill().
|
|
*/
|
|
int
|
|
linux_tkill(struct thread *td, struct linux_tkill_args *args)
|
|
{
|
|
struct thread *tdt;
|
|
ksiginfo_t ksi;
|
|
int sig;
|
|
|
|
#ifdef DEBUG
|
|
if (ldebug(tkill))
|
|
printf(ARGS(tkill, "%i, %i"), args->tid, args->sig);
|
|
#endif
|
|
if (args->tid <= 0)
|
|
return (EINVAL);
|
|
|
|
if (!LINUX_SIG_VALID(args->sig))
|
|
return (EINVAL);
|
|
|
|
sig = linux_to_bsd_signal(args->sig);
|
|
|
|
tdt = linux_tdfind(td, args->tid, -1);
|
|
if (tdt == NULL)
|
|
return (ESRCH);
|
|
|
|
ksiginfo_init(&ksi);
|
|
ksi.ksi_signo = sig;
|
|
ksi.ksi_code = SI_LWP;
|
|
ksi.ksi_errno = 0;
|
|
ksi.ksi_pid = td->td_proc->p_pid;
|
|
ksi.ksi_uid = td->td_proc->p_ucred->cr_ruid;
|
|
return (linux_do_tkill(td, tdt, &ksi));
|
|
}
|
|
|
|
void
|
|
ksiginfo_to_lsiginfo(const ksiginfo_t *ksi, l_siginfo_t *lsi, l_int sig)
|
|
{
|
|
|
|
siginfo_to_lsiginfo(&ksi->ksi_info, lsi, sig);
|
|
}
|
|
|
|
static void
|
|
sicode_to_lsicode(int si_code, int *lsi_code)
|
|
{
|
|
|
|
switch (si_code) {
|
|
case SI_USER:
|
|
*lsi_code = LINUX_SI_USER;
|
|
break;
|
|
case SI_KERNEL:
|
|
*lsi_code = LINUX_SI_KERNEL;
|
|
break;
|
|
case SI_QUEUE:
|
|
*lsi_code = LINUX_SI_QUEUE;
|
|
break;
|
|
case SI_TIMER:
|
|
*lsi_code = LINUX_SI_TIMER;
|
|
break;
|
|
case SI_MESGQ:
|
|
*lsi_code = LINUX_SI_MESGQ;
|
|
break;
|
|
case SI_ASYNCIO:
|
|
*lsi_code = LINUX_SI_ASYNCIO;
|
|
break;
|
|
case SI_LWP:
|
|
*lsi_code = LINUX_SI_TKILL;
|
|
break;
|
|
default:
|
|
*lsi_code = si_code;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
siginfo_to_lsiginfo(const siginfo_t *si, l_siginfo_t *lsi, l_int sig)
|
|
{
|
|
|
|
/* sig alredy converted */
|
|
lsi->lsi_signo = sig;
|
|
sicode_to_lsicode(si->si_code, &lsi->lsi_code);
|
|
|
|
switch (si->si_code) {
|
|
case SI_LWP:
|
|
lsi->lsi_pid = si->si_pid;
|
|
lsi->lsi_uid = si->si_uid;
|
|
break;
|
|
|
|
case SI_TIMER:
|
|
lsi->lsi_int = si->si_value.sival_int;
|
|
lsi->lsi_ptr = PTROUT(si->si_value.sival_ptr);
|
|
lsi->lsi_tid = si->si_timerid;
|
|
break;
|
|
|
|
case SI_QUEUE:
|
|
lsi->lsi_pid = si->si_pid;
|
|
lsi->lsi_uid = si->si_uid;
|
|
lsi->lsi_ptr = PTROUT(si->si_value.sival_ptr);
|
|
break;
|
|
|
|
case SI_ASYNCIO:
|
|
lsi->lsi_int = si->si_value.sival_int;
|
|
lsi->lsi_ptr = PTROUT(si->si_value.sival_ptr);
|
|
break;
|
|
|
|
default:
|
|
switch (sig) {
|
|
case LINUX_SIGPOLL:
|
|
/* XXX si_fd? */
|
|
lsi->lsi_band = si->si_band;
|
|
break;
|
|
|
|
case LINUX_SIGCHLD:
|
|
lsi->lsi_errno = 0;
|
|
lsi->lsi_pid = si->si_pid;
|
|
lsi->lsi_uid = si->si_uid;
|
|
|
|
if (si->si_code == CLD_STOPPED)
|
|
lsi->lsi_status = bsd_to_linux_signal(si->si_status);
|
|
else if (si->si_code == CLD_CONTINUED)
|
|
lsi->lsi_status = bsd_to_linux_signal(SIGCONT);
|
|
else
|
|
lsi->lsi_status = si->si_status;
|
|
break;
|
|
|
|
case LINUX_SIGBUS:
|
|
case LINUX_SIGILL:
|
|
case LINUX_SIGFPE:
|
|
case LINUX_SIGSEGV:
|
|
lsi->lsi_addr = PTROUT(si->si_addr);
|
|
break;
|
|
|
|
default:
|
|
lsi->lsi_pid = si->si_pid;
|
|
lsi->lsi_uid = si->si_uid;
|
|
if (sig >= LINUX_SIGRTMIN) {
|
|
lsi->lsi_int = si->si_value.sival_int;
|
|
lsi->lsi_ptr = PTROUT(si->si_value.sival_ptr);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
lsiginfo_to_ksiginfo(const l_siginfo_t *lsi, ksiginfo_t *ksi, int sig)
|
|
{
|
|
|
|
ksi->ksi_signo = sig;
|
|
ksi->ksi_code = lsi->lsi_code; /* XXX. Convert. */
|
|
ksi->ksi_pid = lsi->lsi_pid;
|
|
ksi->ksi_uid = lsi->lsi_uid;
|
|
ksi->ksi_status = lsi->lsi_status;
|
|
ksi->ksi_addr = PTRIN(lsi->lsi_addr);
|
|
ksi->ksi_info.si_value.sival_int = lsi->lsi_int;
|
|
}
|
|
|
|
int
|
|
linux_rt_sigqueueinfo(struct thread *td, struct linux_rt_sigqueueinfo_args *args)
|
|
{
|
|
l_siginfo_t linfo;
|
|
struct proc *p;
|
|
ksiginfo_t ksi;
|
|
int error;
|
|
int sig;
|
|
|
|
if (!LINUX_SIG_VALID(args->sig))
|
|
return (EINVAL);
|
|
|
|
error = copyin(args->info, &linfo, sizeof(linfo));
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
if (linfo.lsi_code >= 0)
|
|
return (EPERM);
|
|
|
|
sig = linux_to_bsd_signal(args->sig);
|
|
|
|
error = ESRCH;
|
|
if ((p = pfind(args->pid)) != NULL ||
|
|
(p = zpfind(args->pid)) != NULL) {
|
|
error = p_cansignal(td, p, sig);
|
|
if (error != 0) {
|
|
PROC_UNLOCK(p);
|
|
return (error);
|
|
}
|
|
|
|
ksiginfo_init(&ksi);
|
|
lsiginfo_to_ksiginfo(&linfo, &ksi, sig);
|
|
error = tdsendsignal(p, NULL, sig, &ksi);
|
|
PROC_UNLOCK(p);
|
|
}
|
|
|
|
return (error);
|
|
}
|