Home | History | Annotate | Download | only in syscall
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <sys/param.h>
     28 #include <sys/types.h>
     29 #include <sys/bitmap.h>
     30 #include <sys/sysmacros.h>
     31 #include <sys/systm.h>
     32 #include <sys/user.h>
     33 #include <sys/errno.h>
     34 #include <sys/proc.h>
     35 #include <sys/fault.h>
     36 #include <sys/procset.h>
     37 #include <sys/signal.h>
     38 #include <sys/siginfo.h>
     39 #include <sys/time.h>
     40 #include <sys/kmem.h>
     41 #include <sys/schedctl.h>
     42 #include <sys/debug.h>
     43 #include <sys/condvar_impl.h>
     44 #include <sys/model.h>
     45 #include <sys/sdt.h>
     46 #include <sys/zone.h>
     47 
     48 static int
     49 copyout_siginfo(model_t datamodel, k_siginfo_t *ksip, void *uaddr)
     50 {
     51 	zoneid_t zoneid = getzoneid();
     52 
     53 	if (datamodel == DATAMODEL_NATIVE) {
     54 		if (SI_FROMUSER(ksip) && zoneid != GLOBAL_ZONEID &&
     55 		    zoneid != ksip->si_zoneid) {
     56 			k_siginfo_t sani_sip = *ksip;
     57 			sani_sip.si_pid = curproc->p_zone->zone_zsched->p_pid;
     58 			sani_sip.si_uid = 0;
     59 			sani_sip.si_ctid = -1;
     60 			sani_sip.si_zoneid = zoneid;
     61 			if (copyout(&sani_sip, uaddr, sizeof (sani_sip)))
     62 				return (set_errno(EFAULT));
     63 		} else {
     64 			if (copyout(ksip, uaddr, sizeof (*ksip)))
     65 				return (set_errno(EFAULT));
     66 		}
     67 	}
     68 #ifdef _SYSCALL32_IMPL
     69 	else {
     70 		siginfo32_t si32;
     71 
     72 		siginfo_kto32(ksip, &si32);
     73 		if (SI_FROMUSER(ksip) && zoneid != GLOBAL_ZONEID &&
     74 		    zoneid != ksip->si_zoneid) {
     75 			si32.si_pid = curproc->p_zone->zone_zsched->p_pid;
     76 			si32.si_uid = 0;
     77 			si32.si_ctid = -1;
     78 			si32.si_zoneid = zoneid;
     79 		}
     80 		if (copyout(&si32, uaddr, sizeof (si32)))
     81 			return (set_errno(EFAULT));
     82 	}
     83 #endif
     84 	return (ksip->si_signo);
     85 }
     86 
     87 /*
     88  * Wait until a signal within a specified set is posted or until the
     89  * time interval 'timeout' if specified.  The signal is caught but
     90  * not delivered. The value of the signal is returned to the caller.
     91  */
     92 int
     93 sigtimedwait(sigset_t *setp, siginfo_t *siginfop, timespec_t *timeoutp)
     94 {
     95 	sigset_t set;
     96 	k_sigset_t oldmask;
     97 	kthread_t *t = curthread;
     98 	klwp_t *lwp = ttolwp(t);
     99 	proc_t *p = ttoproc(t);
    100 	timespec_t sig_timeout;
    101 	timespec_t *rqtp = NULL;
    102 	int timecheck = 0;
    103 	int ret;
    104 	int error = 0;
    105 	k_siginfo_t info, *infop;
    106 	model_t datamodel = get_udatamodel();
    107 
    108 	if (timeoutp) {
    109 		timespec_t now;
    110 
    111 		timecheck = timechanged;
    112 		gethrestime(&now);
    113 		if (datamodel == DATAMODEL_NATIVE) {
    114 			if (copyin(timeoutp, &sig_timeout,
    115 			    sizeof (sig_timeout)))
    116 				return (set_errno(EFAULT));
    117 		} else {
    118 			timespec32_t timeout32;
    119 
    120 			if (copyin(timeoutp, &timeout32, sizeof (timeout32)))
    121 				return (set_errno(EFAULT));
    122 			TIMESPEC32_TO_TIMESPEC(&sig_timeout, &timeout32)
    123 		}
    124 
    125 		if (itimerspecfix(&sig_timeout))
    126 			return (set_errno(EINVAL));
    127 		/*
    128 		 * Convert the timespec value into absolute time.
    129 		 */
    130 		timespecadd(&sig_timeout, &now);
    131 		rqtp = &sig_timeout;
    132 	}
    133 	if (copyin(setp, &set, sizeof (set)))
    134 		return (set_errno(EFAULT));
    135 	sigutok(&set, &t->t_sigwait);
    136 	if (sigisempty(&t->t_sigwait))
    137 		return (set_errno(EINVAL));
    138 
    139 	mutex_enter(&p->p_lock);
    140 	/*
    141 	 * set the thread's signal mask to unmask
    142 	 * those signals in the specified set.
    143 	 */
    144 	schedctl_finish_sigblock(t);
    145 	oldmask = t->t_hold;
    146 	sigdiffset(&t->t_hold, &t->t_sigwait);
    147 
    148 	/*
    149 	 * Wait until we take a signal or until
    150 	 * the absolute future time is passed.
    151 	 */
    152 	while ((ret = cv_waituntil_sig(&t->t_delay_cv, &p->p_lock,
    153 	    rqtp, timecheck)) > 0)
    154 		continue;
    155 	if (ret == -1)
    156 		error = EAGAIN;
    157 
    158 	/*
    159 	 * Restore thread's signal mask to its previous value.
    160 	 */
    161 	t->t_hold = oldmask;
    162 	t->t_sig_check = 1;	/* so post_syscall sees new t_hold mask */
    163 
    164 	if (error) {
    165 		mutex_exit(&p->p_lock);
    166 		sigemptyset(&t->t_sigwait);
    167 		return (set_errno(error));	/* timer expired */
    168 	}
    169 	/*
    170 	 * Don't bother with signal if it is not in request set.
    171 	 */
    172 	if (lwp->lwp_cursig == 0 ||
    173 	    !sigismember(&t->t_sigwait, lwp->lwp_cursig)) {
    174 		mutex_exit(&p->p_lock);
    175 		/*
    176 		 * lwp_cursig is zero if pokelwps() awakened cv_wait_sig().
    177 		 * This happens if some other thread in this process called
    178 		 * forkall() or exit().
    179 		 */
    180 		sigemptyset(&t->t_sigwait);
    181 		return (set_errno(EINTR));
    182 	}
    183 
    184 	if (lwp->lwp_curinfo)
    185 		infop = &lwp->lwp_curinfo->sq_info;
    186 	else {
    187 		infop = &info;
    188 		bzero(infop, sizeof (info));
    189 		infop->si_signo = lwp->lwp_cursig;
    190 		infop->si_code = SI_NOINFO;
    191 	}
    192 
    193 	lwp->lwp_ru.nsignals++;
    194 	ret = lwp->lwp_cursig;
    195 	DTRACE_PROC2(signal__clear, int, ret, ksiginfo_t *, infop);
    196 	lwp->lwp_cursig = 0;
    197 	lwp->lwp_extsig = 0;
    198 	mutex_exit(&p->p_lock);
    199 
    200 	if (siginfop)
    201 		ret = copyout_siginfo(datamodel, infop, siginfop);
    202 	if (lwp->lwp_curinfo) {
    203 		siginfofree(lwp->lwp_curinfo);
    204 		lwp->lwp_curinfo = NULL;
    205 	}
    206 	sigemptyset(&t->t_sigwait);
    207 	return (ret);
    208 }
    209