Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/kern/sys_sig.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/kern/sys_sig.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.1 retrieving revision 1.1.2.1 diff -u -p -r1.1 -r1.1.2.1 --- src/sys/kern/sys_sig.c 2006/10/21 14:26:41 1.1 +++ src/sys/kern/sys_sig.c 2006/10/21 14:26:41 1.1.2.1 @@ -0,0 +1,774 @@ +/* $NetBSD: sys_sig.c,v 1.1.2.1 2006/10/21 14:26:41 ad Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * 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. + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +/* + * Copyright (c) 1982, 1986, 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * @(#)kern_sig.c 8.14 (Berkeley) 5/14/95 + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: sys_sig.c,v 1.1.2.1 2006/10/21 14:26:41 ad Exp $"); + +#include "opt_ptrace.h" +#include "opt_compat_netbsd.h" +#include "opt_compat_netbsd32.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef COMPAT_16 +/* ARGSUSED */ +int +compat_16_sys___sigaction14(struct lwp *l, void *v, register_t *retval) +{ + struct compat_16_sys___sigaction14_args /* { + syscallarg(int) signum; + syscallarg(const struct sigaction *) nsa; + syscallarg(struct sigaction *) osa; + } */ *uap = v; + struct sigaction nsa, osa; + int error; + + if (SCARG(uap, nsa)) { + error = copyin(SCARG(uap, nsa), &nsa, sizeof(nsa)); + if (error) + return (error); + } + error = sigaction1(l, SCARG(uap, signum), + SCARG(uap, nsa) ? &nsa : 0, SCARG(uap, osa) ? &osa : 0, + NULL, 0); + if (error) + return (error); + if (SCARG(uap, osa)) { + error = copyout(&osa, SCARG(uap, osa), sizeof(osa)); + if (error) + return (error); + } + return (0); +} +#endif + +/* ARGSUSED */ +int +sys___sigaction_sigtramp(struct lwp *l, void *v, register_t *retval) +{ + struct sys___sigaction_sigtramp_args /* { + syscallarg(int) signum; + syscallarg(const struct sigaction *) nsa; + syscallarg(struct sigaction *) osa; + syscallarg(void *) tramp; + syscallarg(int) vers; + } */ *uap = v; + struct sigaction nsa, osa; + int error; + + if (SCARG(uap, nsa)) { + error = copyin(SCARG(uap, nsa), &nsa, sizeof(nsa)); + if (error) + return (error); + } + error = sigaction1(l, SCARG(uap, signum), + SCARG(uap, nsa) ? &nsa : 0, SCARG(uap, osa) ? &osa : 0, + SCARG(uap, tramp), SCARG(uap, vers)); + if (error) + return (error); + if (SCARG(uap, osa)) { + error = copyout(&osa, SCARG(uap, osa), sizeof(osa)); + if (error) + return (error); + } + return (0); +} + +/* + * Manipulate signal mask. Note that we receive new mask, not pointer, and + * return old mask as return value; the library stub does the rest. + */ +int +sys___sigprocmask14(struct lwp *l, void *v, register_t *retval) +{ + struct sys___sigprocmask14_args /* { + syscallarg(int) how; + syscallarg(const sigset_t *) set; + syscallarg(sigset_t *) oset; + } */ *uap = v; + sigset_t nss, oss; + int error; + + if (SCARG(uap, set)) { + error = copyin(SCARG(uap, set), &nss, sizeof(nss)); + if (error) + return (error); + } + error = sigprocmask1(l, SCARG(uap, how), + SCARG(uap, set) ? &nss : 0, SCARG(uap, oset) ? &oss : 0); + if (error) + return (error); + if (SCARG(uap, oset)) { + error = copyout(&oss, SCARG(uap, oset), sizeof(oss)); + if (error) + return (error); + } + return (0); +} + +/* ARGSUSED */ +int +sys___sigpending14(struct lwp *l, void *v, register_t *retval) +{ + struct sys___sigpending14_args /* { + syscallarg(sigset_t *) set; + } */ *uap = v; + sigset_t ss; + + sigpending1(l, &ss); + return (copyout(&ss, SCARG(uap, set), sizeof(ss))); +} + +/* + * Suspend process until signal, providing mask to be set in the meantime. + * Note nonstandard calling convention: libc stub passes mask, not pointer, + * to save a copyin. + */ +/* ARGSUSED */ +int +sys___sigsuspend14(struct lwp *l, void *v, register_t *retval) +{ + struct sys___sigsuspend14_args /* { + syscallarg(const sigset_t *) set; + } */ *uap = v; + sigset_t ss; + int error; + + if (SCARG(uap, set)) { + error = copyin(SCARG(uap, set), &ss, sizeof(ss)); + if (error) + return (error); + } + + return (sigsuspend1(l, SCARG(uap, set) ? &ss : 0)); +} + +/* ARGSUSED */ +int +sys___sigaltstack14(struct lwp *l, void *v, register_t *retval) +{ + struct sys___sigaltstack14_args /* { + syscallarg(const struct sigaltstack *) nss; + syscallarg(struct sigaltstack *) oss; + } */ *uap = v; + struct sigaltstack nss, oss; + int error; + + if (SCARG(uap, nss)) { + error = copyin(SCARG(uap, nss), &nss, sizeof(nss)); + if (error) + return (error); + } + error = sigaltstack1(l, + SCARG(uap, nss) ? &nss : 0, SCARG(uap, oss) ? &oss : 0); + if (error) + return (error); + if (SCARG(uap, oss)) { + error = copyout(&oss, SCARG(uap, oss), sizeof(oss)); + if (error) + return (error); + } + return (0); +} + +/* ARGSUSED */ +int +sys_kill(struct lwp *l, void *v, register_t *retval) +{ + struct sys_kill_args /* { + syscallarg(int) pid; + syscallarg(int) signum; + } */ *uap = v; + struct proc *p; + ksiginfo_t ksi; + int signum = SCARG(uap, signum); + int error; + + if ((u_int)signum >= NSIG) + return (EINVAL); + KSI_INIT(&ksi); + ksi.ksi_signo = signum; + ksi.ksi_code = SI_USER; + ksi.ksi_pid = l->l_proc->p_pid; + ksi.ksi_uid = kauth_cred_geteuid(l->l_cred); + if (SCARG(uap, pid) > 0) { + /* kill single process */ + if ((p = p_find(SCARG(uap, pid), PFIND_UNLOCK_FAIL)) == NULL) + return (ESRCH); + mutex_enter(&p->p_crmutex); + error = kauth_authorize_process(l->l_cred, + KAUTH_PROCESS_CANSIGNAL, p, (void *)(uintptr_t)signum, + NULL, NULL); + if (!error && signum) { + mutex_enter(&p->p_smutex); + kpsignal2(p, &ksi); + mutex_exit(&p->p_smutex); + } + mutex_exit(&p->p_crmutex); + rw_exit(&proclist_lock); + return (0); + } + switch (SCARG(uap, pid)) { + case -1: /* broadcast signal */ + return (killpg1(l, &ksi, 0, 1)); + case 0: /* signal own process group */ + return (killpg1(l, &ksi, 0, 0)); + default: /* negative explicit process group */ + return (killpg1(l, &ksi, -SCARG(uap, pid), 0)); + } + /* NOTREACHED */ +} + +/* + * Nonexistent system call-- signal process (may want to handle it). Flag + * error in case process won't see signal immediately (blocked or ignored). + * + * XXX This should not be here. + */ +#ifndef PTRACE +__weak_alias(sys_ptrace, sys_nosys); +#endif + +/* ARGSUSED */ +int +sys_nosys(struct lwp *l, void *v, register_t *retval) +{ + struct proc *p; + + p = l->l_proc; + psignal(p, SIGSYS); + return (ENOSYS); +} + +/* ARGSUSED */ +int +sys_getcontext(struct lwp *l, void *v, register_t *retval) +{ + struct sys_getcontext_args /* { + syscallarg(struct __ucontext *) ucp; + } */ *uap = v; + ucontext_t uc; + + getucontext(l, &uc); + + return (copyout(&uc, SCARG(uap, ucp), sizeof (*SCARG(uap, ucp)))); +} + +/* ARGSUSED */ +int +sys_setcontext(struct lwp *l, void *v, register_t *retval) +{ + struct sys_setcontext_args /* { + syscallarg(const ucontext_t *) ucp; + } */ *uap = v; + ucontext_t uc; + int error; + + if (SCARG(uap, ucp) == NULL) { /* i.e. end of uc_link chain */ + /* Acquire the sched state mutex. exit1() will release it. */ + mutex_enter(&l->l_proc->p_smutex); + exit1(l, W_EXITCODE(0, 0)); + } else if ((error = copyin(SCARG(uap, ucp), &uc, sizeof (uc))) != 0 || + (error = setucontext(l, &uc)) != 0) + return (error); + + return (EJUSTRETURN); +} + +/* + * sigtimedwait(2) system call, used also for implementation + * of sigwaitinfo() and sigwait(). + * + * This only handles single LWP in signal wait. libpthread provides + * it's own sigtimedwait() wrapper to DTRT WRT individual threads. + */ +int +sys___sigtimedwait(struct lwp *l, void *v, register_t *retval) +{ + + return __sigtimedwait1(l, v, retval, copyout, copyin, copyout); +} + +int +sigaction1(struct lwp *l, int signum, const struct sigaction *nsa, + struct sigaction *osa, const void *tramp, int vers) +{ + struct proc *p; + struct sigacts *ps; + sigset_t tset; + int prop; + + if (signum <= 0 || signum >= NSIG) + return (EINVAL); + + p = l->l_proc; + + /* + * Trampoline ABI version 0 is reserved for the legacy kernel + * provided on-stack trampoline. Conversely, if we are using a + * non-0 ABI version, we must have a trampoline. Only validate the + * vers if a new sigaction was supplied. Emulations use legacy + * kernel trampolines with version 0, alternatively check for that + * too. + */ + if ((vers != 0 && tramp == NULL) || +#ifdef SIGTRAMP_VALID + (nsa != NULL && + ((vers == 0) ? + (p->p_emul->e_sigcode == NULL) : + !SIGTRAMP_VALID(vers))) || +#endif + (vers == 0 && tramp != NULL)) { + return (EINVAL); + } + + mutex_enter(&p->p_smutex); + ps = p->p_sigacts; + if (osa) + *osa = SIGACTION_PS(ps, signum); + if (!nsa) { + mutex_exit(&p->p_smutex); + return (0); + } + + prop = sigprop[signum]; + if ((nsa->sa_flags & ~SA_ALLBITS) || (prop & SA_CANTMASK)) { + mutex_exit(&p->p_smutex); + return (EINVAL); + } + + SIGACTION_PS(ps, signum) = *nsa; + ps->sa_sigdesc[signum].sd_tramp = tramp; + ps->sa_sigdesc[signum].sd_vers = vers; + sigminusset(&sigcantmask, &SIGACTION_PS(ps, signum).sa_mask); + + if ((prop & SA_NORESET) != 0) + SIGACTION_PS(ps, signum).sa_flags &= ~SA_RESETHAND; + + if (signum == SIGCHLD) { + if (nsa->sa_flags & SA_NOCLDSTOP) + p->p_flag |= P_NOCLDSTOP; + else + p->p_flag &= ~P_NOCLDSTOP; + if (nsa->sa_flags & SA_NOCLDWAIT) { + /* + * Paranoia: since SA_NOCLDWAIT is implemented by + * reparenting the dying child to PID 1 (and trust + * it to reap the zombie), PID 1 itself is forbidden + * to set SA_NOCLDWAIT. + */ + if (p->p_pid == 1) + p->p_flag &= ~P_NOCLDWAIT; + else + p->p_flag |= P_NOCLDWAIT; + } else + p->p_flag &= ~P_NOCLDWAIT; + + if (nsa->sa_handler == SIG_IGN) { + /* + * Paranoia: same as above. + */ + if (p->p_pid == 1) + p->p_flag &= ~P_CLDSIGIGN; + else + p->p_flag |= P_CLDSIGIGN; + } else + p->p_flag &= ~P_CLDSIGIGN; + } + + if ((nsa->sa_flags & SA_NODEFER) == 0) + sigaddset(&SIGACTION_PS(ps, signum).sa_mask, signum); + else + sigdelset(&SIGACTION_PS(ps, signum).sa_mask, signum); + + /* + * Set bit in p_sigctx.ps_sigignore for signals that are set to + * SIG_IGN, and for signals set to SIG_DFL where the default is to + * ignore. However, don't put SIGCONT in p_sigctx.ps_sigignore, as + * we have to restart the process. + */ + if (nsa->sa_handler == SIG_IGN || + (nsa->sa_handler == SIG_DFL && (prop & SA_IGNORE) != 0)) { + /* Never to be seen again. */ + sigemptyset(&tset); + sigaddset(&tset, signum); + sigclearall(p, &tset); + if (signum != SIGCONT) { + /* Easier in psignal */ + sigaddset(&p->p_sigctx.ps_sigignore, signum); + } + sigdelset(&p->p_sigctx.ps_sigcatch, signum); + } else { + sigdelset(&p->p_sigctx.ps_sigignore, signum); + if (nsa->sa_handler == SIG_DFL) + sigdelset(&p->p_sigctx.ps_sigcatch, signum); + else + sigaddset(&p->p_sigctx.ps_sigcatch, signum); + } + + /* + * Previously held signals may now have become visible. Ensure that + * we check for them before returning to userspace. + */ + lwp_lock(l); + signotify(l); + lwp_unlock(l); + + mutex_exit(&p->p_smutex); + return (0); +} + +int +sigprocmask1(struct lwp *l, int how, const sigset_t *nss, sigset_t *oss) +{ + struct proc *p = l->l_proc; + int more; + + mutex_enter(&p->p_smutex); + + if (oss) + *oss = l->l_sigmask; + if (nss) { + switch (how) { + case SIG_BLOCK: + sigplusset(nss, &l->l_sigmask); + more = 0; + break; + case SIG_UNBLOCK: + sigminusset(nss, &l->l_sigmask); + more = 1; + break; + case SIG_SETMASK: + l->l_sigmask = *nss; + more = 1; + break; + default: + mutex_exit(&p->p_smutex); + return (EINVAL); + } + sigminusset(&sigcantmask, &l->l_sigmask); + if (more) { + /* + * Pinch any signals from the per-process pending + * list that are now of interest to us. + */ + sigpinch(&p->p_sigpend, &l->l_sigpend, &l->l_sigmask); + + /* + * Check for pending signals on return to user. + */ + lwp_lock(l); + signotify(l); + lwp_unlock(l); + } + } + + mutex_exit(&p->p_smutex); + + return (0); +} + +void +sigpending1(struct lwp *l, sigset_t *ss) +{ + struct proc *p = l->l_proc; + + mutex_enter(&p->p_smutex); + *ss = l->l_sigpend.sp_set; + sigplusset(&p->p_sigpend.sp_set, ss); + sigminusset(&l->l_sigmask, ss); + mutex_exit(&p->p_smutex); +} + +int +sigsuspend1(struct lwp *l, const sigset_t *ss) +{ + struct proc *p; + struct sigacts *ps; + + p = l->l_proc; + ps = p->p_sigacts; + + mutex_enter(&p->p_smutex); + + if (ss) { + /* + * When returning from sigpause, we want + * the old mask to be restored after the + * signal handler has finished. Thus, we + * save it here and mark the sigctx structure + * to indicate this. + */ + l->l_sigoldmask = l->l_sigmask; + l->l_sigrestore = 1; + l->l_sigmask = *ss; + sigminusset(&sigcantmask, &l->l_sigmask); + lwp_lock(l); + signotify(l); + lwp_unlock(l); + } + + while (mtsleep((caddr_t) ps, PPAUSE|PCATCH, "pause", 0, + &p->p_smutex) == 0) + /* void */; + + mutex_exit(&p->p_smutex); + + /* always return EINTR rather than ERESTART... */ + return (EINTR); +} + +int +sigaltstack1(struct lwp *l, const struct sigaltstack *nss, + struct sigaltstack *oss) +{ + + if (oss) + *oss = l->l_sigstk; + + if (nss) { + if (nss->ss_flags & ~SS_ALLBITS) + return (EINVAL); + + if (nss->ss_flags & SS_DISABLE) { + if (l->l_sigstk.ss_flags & SS_ONSTACK) + return (EINVAL); + } else { + if (nss->ss_size < MINSIGSTKSZ) + return (ENOMEM); + } + l->l_sigstk = *nss; + } + + return (0); +} + +int +__sigtimedwait1(struct lwp *l, void *v, register_t *retval, + copyout_t put_info, copyin_t fetch_timeout, copyout_t put_timeout) +{ + struct sys___sigtimedwait_args /* { + syscallarg(const sigset_t *) set; + syscallarg(siginfo_t *) info; + syscallarg(struct timespec *) timeout; + } */ *uap = v; + sigset_t *waitset; + struct proc *p = l->l_proc; + int error, signum; + int timo = 0; + struct timespec ts, tsstart, tsnow; + ksiginfo_t *ksi; + + memset(&tsstart, 0, sizeof tsstart); /* XXX gcc */ + + /* + * Calculate timeout, if it was specified. + */ + if (SCARG(uap, timeout)) { + uint64_t ms; + + if ((error = (*fetch_timeout)(SCARG(uap, timeout), &ts, sizeof(ts)))) + return (error); + + ms = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000); + timo = mstohz(ms); + if (timo == 0 && ts.tv_sec == 0 && ts.tv_nsec > 0) + timo = 1; + if (timo <= 0) + return (EAGAIN); + + /* + * Remember current uptime, it would be used in + * ECANCELED/ERESTART case. + */ + getnanouptime(&tsstart); + } + + MALLOC(waitset, sigset_t *, sizeof(sigset_t), M_TEMP, M_WAITOK); + if ((error = copyin(SCARG(uap, set), waitset, sizeof(sigset_t)))) { + FREE(waitset, M_TEMP); + return (error); + } + + /* + * Silently ignore SA_CANTMASK signals. psignal1() would ignore + * SA_CANTMASK signals in waitset, we do this only for the below + * siglist check. + */ + sigminusset(&sigcantmask, waitset); + + /* + * Allocate a ksi up front. We can't sleep with the mutex held. + */ + ksi = pool_get(&ksiginfo_pool, PR_WAITOK); + + /* + * First scan the per-proc and per-LWP lists and check if there is + * signal from our waitset already pending. + */ + mutex_enter(&p->p_smutex); + + if ((signum = sigget(&p->p_sigpend, ksi, 0, waitset)) == 0) + signum = sigget(&l->l_sigpend, ksi, 0, waitset); + + if (signum != 0) { + /* + * We found a pending signal - copy it out to the user. + */ + mutex_exit(&p->p_smutex); + error = (*put_info)(&ksi->ksi_info, SCARG(uap, info), + sizeof(ksi->ksi_info)); + pool_put(&ksiginfo_pool, ksi); + return error; + } + + /* + * Set up the sigwait list. Pass pointer to malloced memory here; + * it's not possible to pass pointer to a structure on current + * process's stack, the current LWP might be swapped out when the + * when the signal is delivered. + */ + l->l_sigwaited = ksi; + l->l_sigwait = waitset; + + /* + * Wait for signal to arrive. We can either be woken up or time out. + */ + error = mtsleep(&l->l_sigwait, PPAUSE|PCATCH, "sigwait", timo, + &p->p_smutex); + + /* + * Need to find out if we woke as a result of lwp_wakeup() or a + * signal outside our wait set. + */ + if (l->l_sigwaited != NULL) { + if (error == EINTR) { + /* wakeup via _lwp_wakeup() */ + error = ECANCELED; + } else if (!error) { + /* spurious wakeup - arrange for syscall restart */ + error = ERESTART; + } + } + + /* + * Clear the sigwait indication and unlock. + */ + l->l_sigwait = NULL; + l->l_sigwaited = NULL; + mutex_exit(&p->p_smutex); + + /* + * If the sleep was interrupted (either by signal or wakeup), update + * the timeout and copyout new value back. It would be used when + * the syscall would be restarted or called again. + */ + if (timo && (error == ERESTART || error == ECANCELED)) { + getnanouptime(&tsnow); + + /* compute how much time has passed since start */ + timespecsub(&tsnow, &tsstart, &tsnow); + /* substract passed time from timeout */ + timespecsub(&ts, &tsnow, &ts); + + if (ts.tv_sec < 0) + error = EAGAIN; + else { + /* copy updated timeout to userland */ + error = (*put_timeout)(&ts, SCARG(uap, timeout), + sizeof(ts)); + } + } + + /* + * If a signal from the wait set arrived, copy it to userland. + * Copy only the used part of siginfo, the padding part is + * left unchanged (userland is not supposed to touch it anyway). + */ + FREE(waitset, M_TEMP); + pool_put(&ksiginfo_pool, ksi); + + if (error == 0) + error = (*put_info)(&ksi->ksi_info, SCARG(uap, info), + sizeof(ksi->ksi_info)); + + return error; +}