signotify.c revision 7fabbdb7f3578a397de372ae3fac157f486cc4bc
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/proc.h>
#include <sys/procset.h>
#include <sys/fault.h>
#include <sys/signal.h>
#include <sys/siginfo.h>
#include <sys/schedctl.h>
#include <vm/as.h>
#include <sys/debug.h>
#include <sys/contract/process_impl.h>
/*ARGSUSED*/
static int
copyin_siginfo(model_t datamodel, void *uaddr, k_siginfo_t *ksip)
{
#ifdef _SYSCALL32_IMPL
int ret;
if (datamodel == DATAMODEL_NATIVE) {
#endif
return (copyin(uaddr, ksip, sizeof (k_siginfo_t)));
#ifdef _SYSCALL32_IMPL
} else {
siginfo32_t si32;
if (ret = copyin(uaddr, &si32, sizeof (si32)))
return (ret);
siginfo_32tok(&si32, ksip);
}
return (0);
#endif
}
/*
* To find secured 64 bit id for signotify() call
* This depends upon as_getmemid() which returns
* unique vnode/offset for a user virtual address.
*/
static u_longlong_t
get_sigid(proc_t *p, caddr_t addr)
{
u_longlong_t snid = 0;
memid_t memid;
quad_t *tquad = (quad_t *)&snid;
if (!as_getmemid(p->p_as, addr, &memid)) {
tquad->val[0] = (int)memid.val[0];
tquad->val[1] = (int)memid.val[1];
}
return (snid);
}
#define SIGN_PTR(p, n) &((signotifyq_t *)(&p->p_signhdr[1]))[n];
int
signotify(int cmd, siginfo_t *siginfo, signotify_id_t *sn_id)
{
k_siginfo_t info;
signotify_id_t id;
proc_t *p;
proc_t *cp = curproc;
signotifyq_t *snqp;
struct cred *cr;
sigqueue_t *sqp;
sigqhdr_t *sqh;
u_longlong_t sid;
model_t datamodel = get_udatamodel();
if (copyin(sn_id, &id, sizeof (signotify_id_t)))
return (set_errno(EFAULT));
if (id.sn_index >= _SIGNOTIFY_MAX || id.sn_index < 0)
return (set_errno(EINVAL));
switch (cmd) {
case SN_PROC:
/* get snid for the given user address of signotifyid_t */
sid = get_sigid(cp, (caddr_t)sn_id);
if (id.sn_pid > 0) {
mutex_enter(&pidlock);
if ((p = prfind(id.sn_pid)) != NULL) {
mutex_enter(&p->p_lock);
if (p->p_signhdr != NULL) {
snqp = SIGN_PTR(p, id.sn_index);
if (snqp->sn_snid == sid) {
mutex_exit(&p->p_lock);
mutex_exit(&pidlock);
return (set_errno(EBUSY));
}
}
mutex_exit(&p->p_lock);
}
mutex_exit(&pidlock);
}
if (copyin_siginfo(datamodel, siginfo, &info))
return (set_errno(EFAULT));
/* The si_code value must indicate the signal will be queued */
if (!sigwillqueue(info.si_signo, info.si_code))
return (set_errno(EINVAL));
if (cp->p_signhdr == NULL) {
/* Allocate signotify pool first time */
sqh = sigqhdralloc(sizeof (signotifyq_t),
_SIGNOTIFY_MAX);
mutex_enter(&cp->p_lock);
if (cp->p_signhdr == NULL) {
/* hang the pool head on proc */
cp->p_signhdr = sqh;
} else {
/* another lwp allocated the pool, free ours */
sigqhdrfree(sqh);
}
} else {
mutex_enter(&cp->p_lock);
}
sqp = sigqalloc(cp->p_signhdr);
if (sqp == NULL) {
mutex_exit(&cp->p_lock);
return (set_errno(EAGAIN));
}
cr = CRED();
sqp->sq_info = info;
sqp->sq_info.si_pid = cp->p_pid;
sqp->sq_info.si_ctid = PRCTID(cp);
sqp->sq_info.si_zoneid = getzoneid();
sqp->sq_info.si_uid = crgetruid(cr);
/* fill the signotifyq_t fields */
((signotifyq_t *)sqp)->sn_snid = sid;
mutex_exit(&cp->p_lock);
/* complete the signotify_id_t fields */
id.sn_index = (signotifyq_t *)sqp - SIGN_PTR(cp, 0);
id.sn_pid = cp->p_pid;
break;
case SN_CANCEL:
case SN_SEND:
sid = get_sigid(cp, (caddr_t)sn_id);
mutex_enter(&pidlock);
if ((id.sn_pid <= 0) || ((p = prfind(id.sn_pid)) == NULL)) {
mutex_exit(&pidlock);
return (set_errno(EINVAL));
}
mutex_enter(&p->p_lock);
mutex_exit(&pidlock);
if (p->p_signhdr == NULL) {
mutex_exit(&p->p_lock);
return (set_errno(EINVAL));
}
snqp = SIGN_PTR(p, id.sn_index);
if (snqp->sn_snid == 0) {
mutex_exit(&p->p_lock);
return (set_errno(EINVAL));
}
if (snqp->sn_snid != sid) {
mutex_exit(&p->p_lock);
return (set_errno(EINVAL));
}
snqp->sn_snid = 0;
/* cmd == SN_CANCEL or signo == 0 (SIGEV_NONE) */
if (((sigqueue_t *)snqp)->sq_info.si_signo <= 0)
cmd = SN_CANCEL;
sigqsend(cmd, p, 0, (sigqueue_t *)snqp);
mutex_exit(&p->p_lock);
id.sn_pid = 0;
id.sn_index = 0;
break;
default :
return (set_errno(EINVAL));
}
if (copyout(&id, sn_id, sizeof (signotify_id_t)))
return (set_errno(EFAULT));
return (0);
}
int
sigresend(int sig, siginfo_t *siginfo, sigset_t *mask)
{
kthread_t *t = curthread;
proc_t *p = ttoproc(t);
klwp_t *lwp = ttolwp(t);
sigqueue_t *sqp = kmem_zalloc(sizeof (*sqp), KM_SLEEP);
sigset_t set;
k_sigset_t kset;
int error;
if (sig <= 0 || sig >= NSIG || sigismember(&cantmask, sig)) {
error = EINVAL;
goto bad;
}
if (siginfo == NULL) {
sqp->sq_info.si_signo = sig;
sqp->sq_info.si_code = SI_NOINFO;
} else {
if (copyin_siginfo(get_udatamodel(), siginfo, &sqp->sq_info)) {
error = EFAULT;
goto bad;
}
if (sqp->sq_info.si_signo != sig) {
error = EINVAL;
goto bad;
}
}
if (copyin(mask, &set, sizeof (set))) {
error = EFAULT;
goto bad;
}
sigutok(&set, &kset);
mutex_enter(&p->p_lock);
if (lwp->lwp_cursig || lwp->lwp_curinfo) {
mutex_exit(&p->p_lock);
t->t_sig_check = 1;
error = EAGAIN;
goto bad;
}
lwp->lwp_cursig = sig;
lwp->lwp_curinfo = sqp;
schedctl_finish_sigblock(t);
t->t_hold = kset;
mutex_exit(&p->p_lock);
t->t_sig_check = 1;
return (0);
bad:
kmem_free(sqp, sizeof (*sqp));
return (set_errno(error));
}