/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include /*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)); }