#include "opt_ktrace.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/sysmsg.h>
#include <sys/signalvar.h>
#include <sys/resourcevar.h>
#include <sys/vnode.h>
#include <sys/event.h>
#include <sys/proc.h>
#include <sys/nlookup.h>
#include <sys/pioctl.h>
#include <sys/acct.h>
#include <sys/fcntl.h>
#include <sys/lock.h>
#include <sys/wait.h>
#include <sys/ktrace.h>
#include <sys/syslog.h>
#include <sys/stat.h>
#include <sys/sysent.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <sys/interrupt.h>
#include <sys/unistd.h>
#include <sys/kern_syscall.h>
#include <sys/vkernel.h>
#include <sys/signal2.h>
#include <sys/thread2.h>
#include <sys/spinlock2.h>
#include <machine/cpu.h>
#include <machine/smp.h>
static int coredump(struct lwp *, int);
static char *expand_name(const char *, uid_t, pid_t);
static int dokillpg(int sig, int pgid, int all);
static int sig_ffs(sigset_t *set);
static int sigprop(int sig);
static void lwp_signotify(struct lwp *lp);
static void lwp_signotify_remote(void *arg);
static int kern_sigtimedwait(sigset_t set, siginfo_t *info,
struct timespec *timeout);
static void proc_stopwait(struct proc *p);
static int filt_sigattach(struct knote *kn);
static void filt_sigdetach(struct knote *kn);
static int filt_signal(struct knote *kn, long hint);
struct filterops sig_filtops =
{ FILTEROP_MPSAFE, filt_sigattach, filt_sigdetach, filt_signal };
static int kern_logsigexit = 1;
SYSCTL_INT(_kern, KERN_LOGSIGEXIT, logsigexit, CTLFLAG_RW,
&kern_logsigexit, 0,
"Log processes quitting on abnormal signals to syslog(3)");
#define CANSIGIO(ruid, uc, q) \
((uc)->cr_uid == 0 || \
(ruid) == (q)->p_ucred->cr_ruid || \
(uc)->cr_uid == (q)->p_ucred->cr_ruid || \
(ruid) == (q)->p_ucred->cr_uid || \
(uc)->cr_uid == (q)->p_ucred->cr_uid)
int sugid_coredump;
SYSCTL_INT(_kern, OID_AUTO, sugid_coredump, CTLFLAG_RW,
&sugid_coredump, 0, "Enable coredumping set user/group ID processes");
static int do_coredump = 1;
SYSCTL_INT(_kern, OID_AUTO, coredump, CTLFLAG_RW,
&do_coredump, 0, "Enable/Disable coredumps");
#define SA_KILL 0x01
#define SA_CORE 0x02
#define SA_STOP 0x04
#define SA_TTYSTOP 0x08
#define SA_IGNORE 0x10
#define SA_CONT 0x20
#define SA_CANTMASK 0x40
#define SA_CKPT 0x80
static int sigproptbl[NSIG] = {
SA_KILL,
SA_KILL,
SA_KILL|SA_CORE,
SA_KILL|SA_CORE,
SA_KILL|SA_CORE,
SA_KILL|SA_CORE,
SA_KILL|SA_CORE,
SA_KILL|SA_CORE,
SA_KILL,
SA_KILL|SA_CORE,
SA_KILL|SA_CORE,
SA_KILL|SA_CORE,
SA_KILL,
SA_KILL,
SA_KILL,
SA_IGNORE,
SA_STOP,
SA_STOP|SA_TTYSTOP,
SA_IGNORE|SA_CONT,
SA_IGNORE,
SA_STOP|SA_TTYSTOP,
SA_STOP|SA_TTYSTOP,
SA_IGNORE,
SA_KILL,
SA_KILL,
SA_KILL,
SA_KILL,
SA_IGNORE,
SA_IGNORE,
SA_KILL,
SA_KILL,
SA_IGNORE,
SA_CKPT,
SA_KILL|SA_CKPT,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
SA_IGNORE,
};
__read_mostly sigset_t sigcantmask_mask;
static __inline int
sigprop(int sig)
{
if (sig > 0 && sig < NSIG)
return (sigproptbl[_SIG_IDX(sig)]);
return (0);
}
static __inline int
sig_ffs(sigset_t *set)
{
int i;
for (i = 0; i < _SIG_WORDS; i++)
if (set->__bits[i])
return (ffs(set->__bits[i]) + (i * 32));
return (0);
}
static __inline void
sigsetfrompid(thread_t td, struct proc *p, int sig)
{
struct sigacts *sap;
if ((sap = p->p_sigacts) == NULL)
return;
if (td->td_proc) {
sap->ps_frominfo[sig].pid = td->td_proc->p_pid;
sap->ps_frominfo[sig].uid = td->td_ucred->cr_uid;
} else {
sap->ps_frominfo[sig].pid = 0;
sap->ps_frominfo[sig].uid = 0;
}
}
int
kern_sigaction(int sig, struct sigaction *act, struct sigaction *oact)
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
struct lwp *lp;
struct sigacts *ps = p->p_sigacts;
if (!_SIG_VALID(sig))
return (EINVAL);
lwkt_gettoken(&p->p_token);
if (oact) {
oact->sa_handler = ps->ps_sigact[_SIG_IDX(sig)];
oact->sa_mask = ps->ps_catchmask[_SIG_IDX(sig)];
oact->sa_flags = 0;
if (SIGISMEMBER(ps->ps_sigonstack, sig))
oact->sa_flags |= SA_ONSTACK;
if (!SIGISMEMBER(ps->ps_sigintr, sig))
oact->sa_flags |= SA_RESTART;
if (SIGISMEMBER(ps->ps_sigreset, sig))
oact->sa_flags |= SA_RESETHAND;
if (SIGISMEMBER(ps->ps_signodefer, sig))
oact->sa_flags |= SA_NODEFER;
if (SIGISMEMBER(ps->ps_siginfo, sig))
oact->sa_flags |= SA_SIGINFO;
if (sig == SIGCHLD) {
if (p->p_sigacts->ps_flag & PS_NOCLDSTOP)
oact->sa_flags |= SA_NOCLDSTOP;
if (p->p_sigacts->ps_flag & PS_NOCLDWAIT)
oact->sa_flags |= SA_NOCLDWAIT;
}
}
if (act) {
if (sig == SIGKILL || sig == SIGSTOP) {
if (act->sa_handler != SIG_DFL) {
lwkt_reltoken(&p->p_token);
return (EINVAL);
}
}
ps->ps_catchmask[_SIG_IDX(sig)] = act->sa_mask;
SIG_CANTMASK(ps->ps_catchmask[_SIG_IDX(sig)]);
if (act->sa_flags & SA_SIGINFO) {
ps->ps_sigact[_SIG_IDX(sig)] =
(__sighandler_t *)act->sa_sigaction;
SIGADDSET(ps->ps_siginfo, sig);
} else {
ps->ps_sigact[_SIG_IDX(sig)] = act->sa_handler;
SIGDELSET(ps->ps_siginfo, sig);
}
if (!(act->sa_flags & SA_RESTART))
SIGADDSET(ps->ps_sigintr, sig);
else
SIGDELSET(ps->ps_sigintr, sig);
if (act->sa_flags & SA_ONSTACK)
SIGADDSET(ps->ps_sigonstack, sig);
else
SIGDELSET(ps->ps_sigonstack, sig);
if (act->sa_flags & SA_RESETHAND)
SIGADDSET(ps->ps_sigreset, sig);
else
SIGDELSET(ps->ps_sigreset, sig);
if (act->sa_flags & SA_NODEFER)
SIGADDSET(ps->ps_signodefer, sig);
else
SIGDELSET(ps->ps_signodefer, sig);
if (sig == SIGCHLD) {
if (act->sa_flags & SA_NOCLDSTOP)
p->p_sigacts->ps_flag |= PS_NOCLDSTOP;
else
p->p_sigacts->ps_flag &= ~PS_NOCLDSTOP;
if (act->sa_flags & SA_NOCLDWAIT) {
if (p->p_pid == 1)
p->p_sigacts->ps_flag &= ~PS_NOCLDWAIT;
else
p->p_sigacts->ps_flag |= PS_NOCLDWAIT;
} else {
p->p_sigacts->ps_flag &= ~PS_NOCLDWAIT;
}
if (ps->ps_sigact[_SIG_IDX(SIGCHLD)] == SIG_IGN)
ps->ps_flag |= PS_CLDSIGIGN;
else
ps->ps_flag &= ~PS_CLDSIGIGN;
}
if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN ||
(sigprop(sig) & SA_IGNORE &&
ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL)) {
SIGDELSET_ATOMIC(p->p_siglist, sig);
FOREACH_LWP_IN_PROC(lp, p) {
spin_lock(&lp->lwp_spin);
SIGDELSET(lp->lwp_siglist, sig);
spin_unlock(&lp->lwp_spin);
}
if (sig != SIGCONT) {
SIGADDSET(p->p_sigignore, sig);
}
SIGDELSET(p->p_sigcatch, sig);
} else {
SIGDELSET(p->p_sigignore, sig);
if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL)
SIGDELSET(p->p_sigcatch, sig);
else
SIGADDSET(p->p_sigcatch, sig);
}
}
lwkt_reltoken(&p->p_token);
return (0);
}
int
sys_sigaction(struct sysmsg *sysmsg, const struct sigaction_args *uap)
{
struct sigaction act, oact;
struct sigaction *actp, *oactp;
int error;
actp = (uap->act != NULL) ? &act : NULL;
oactp = (uap->oact != NULL) ? &oact : NULL;
if (actp) {
error = copyin(uap->act, actp, sizeof(act));
if (error)
return (error);
}
error = kern_sigaction(uap->sig, actp, oactp);
if (oactp && !error) {
error = copyout(oactp, uap->oact, sizeof(oact));
}
return (error);
}
void
siginit(struct proc *p)
{
int i;
for (i = 1; i <= NSIG; i++) {
if ((sigprop(i) & SA_IGNORE) && i != SIGCONT)
SIGADDSET(p->p_sigignore, i);
}
SIGSETOR_CANTMASK(sigcantmask_mask);
}
void
execsigs(struct proc *p)
{
struct sigacts *ps = p->p_sigacts;
struct lwp *lp;
int sig;
lp = ONLY_LWP_IN_PROC(p);
while (SIGNOTEMPTY(p->p_sigcatch)) {
sig = sig_ffs(&p->p_sigcatch);
SIGDELSET(p->p_sigcatch, sig);
if (sigprop(sig) & SA_IGNORE) {
if (sig != SIGCONT)
SIGADDSET(p->p_sigignore, sig);
SIGDELSET_ATOMIC(p->p_siglist, sig);
SIGDELSET(lp->lwp_siglist, sig);
}
ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL;
}
lp->lwp_sigstk.ss_flags = SS_DISABLE;
lp->lwp_sigstk.ss_size = 0;
lp->lwp_sigstk.ss_sp = NULL;
lp->lwp_flags &= ~LWP_ALTSTACK;
p->p_sigacts->ps_flag &= ~(PS_NOCLDWAIT | PS_CLDSIGIGN);
if (ps->ps_sigact[_SIG_IDX(SIGCHLD)] == SIG_IGN)
ps->ps_sigact[_SIG_IDX(SIGCHLD)] = SIG_DFL;
}
int
kern_sigprocmask(int how, sigset_t *set, sigset_t *oset)
{
struct thread *td = curthread;
struct lwp *lp = td->td_lwp;
struct proc *p = td->td_proc;
int error;
lwkt_gettoken(&p->p_token);
if (oset != NULL)
*oset = lp->lwp_sigmask;
error = 0;
if (set != NULL) {
switch (how) {
case SIG_BLOCK:
SIG_CANTMASK(*set);
SIGSETOR(lp->lwp_sigmask, *set);
break;
case SIG_UNBLOCK:
SIGSETNAND(lp->lwp_sigmask, *set);
break;
case SIG_SETMASK:
SIG_CANTMASK(*set);
lp->lwp_sigmask = *set;
sigirefs_wait(p);
break;
default:
error = EINVAL;
break;
}
}
lwkt_reltoken(&p->p_token);
return (error);
}
int
sys_sigprocmask(struct sysmsg *sysmsg, const struct sigprocmask_args *uap)
{
sigset_t set, oset;
sigset_t *setp, *osetp;
int error;
setp = (uap->set != NULL) ? &set : NULL;
osetp = (uap->oset != NULL) ? &oset : NULL;
if (setp) {
error = copyin(uap->set, setp, sizeof(set));
if (error)
return (error);
}
error = kern_sigprocmask(uap->how, setp, osetp);
if (osetp && !error) {
error = copyout(osetp, uap->oset, sizeof(oset));
}
return (error);
}
int
kern_sigpending(sigset_t *set)
{
struct lwp *lp = curthread->td_lwp;
*set = lwp_sigpend(lp);
return (0);
}
int
sys_sigpending(struct sysmsg *sysmsg, const struct sigpending_args *uap)
{
sigset_t set;
int error;
error = kern_sigpending(&set);
if (error == 0)
error = copyout(&set, uap->set, sizeof(set));
return (error);
}
int
kern_sigsuspend(sigset_t *set)
{
struct thread *td = curthread;
struct lwp *lp = td->td_lwp;
struct proc *p = td->td_proc;
struct sigacts *ps = p->p_sigacts;
lwkt_gettoken(&lp->lwp_token);
lp->lwp_oldsigmask = lp->lwp_sigmask;
lp->lwp_flags |= LWP_OLDMASK;
SIG_CANTMASK(*set);
lp->lwp_sigmask = *set;
lwkt_reltoken(&lp->lwp_token);
sigirefs_wait(p);
while (tsleep(ps, PCATCH, "pause", 0) == 0)
;
return (EINTR);
}
int
sys_sigsuspend(struct sysmsg *sysmsg, const struct sigsuspend_args *uap)
{
sigset_t mask;
int error;
error = copyin(uap->sigmask, &mask, sizeof(mask));
if (error)
return (error);
error = kern_sigsuspend(&mask);
return (error);
}
int
kern_sigaltstack(stack_t *ss, stack_t *oss)
{
struct thread *td = curthread;
struct lwp *lp = td->td_lwp;
struct proc *p = td->td_proc;
if ((lp->lwp_flags & LWP_ALTSTACK) == 0)
lp->lwp_sigstk.ss_flags |= SS_DISABLE;
if (oss)
*oss = lp->lwp_sigstk;
if (ss) {
if (ss->ss_flags & ~SS_DISABLE)
return (EINVAL);
if (ss->ss_flags & SS_DISABLE) {
if (lp->lwp_sigstk.ss_flags & SS_ONSTACK)
return (EPERM);
lp->lwp_flags &= ~LWP_ALTSTACK;
lp->lwp_sigstk.ss_flags = ss->ss_flags;
} else {
if (ss->ss_size < p->p_sysent->sv_minsigstksz)
return (ENOMEM);
lp->lwp_flags |= LWP_ALTSTACK;
lp->lwp_sigstk = *ss;
}
}
return (0);
}
int
sys_sigaltstack(struct sysmsg *sysmsg, const struct sigaltstack_args *uap)
{
stack_t ss, oss;
int error;
if (uap->ss) {
error = copyin(uap->ss, &ss, sizeof(ss));
if (error)
return (error);
}
error = kern_sigaltstack(uap->ss ? &ss : NULL, uap->oss ? &oss : NULL);
if (error == 0 && uap->oss)
error = copyout(&oss, uap->oss, sizeof(*uap->oss));
return (error);
}
struct killpg_info {
int nfound;
int sig;
};
static int killpg_all_callback(struct proc *p, void *data);
static int
dokillpg(int sig, int pgid, int all)
{
struct killpg_info info;
struct proc *cp = curproc;
struct proc *p;
struct pgrp *pgrp;
info.nfound = 0;
info.sig = sig;
if (all) {
allproc_scan(killpg_all_callback, &info, 0);
} else {
if (pgid == 0) {
pgrp = cp->p_pgrp;
pgref(pgrp);
} else {
pgrp = pgfind(pgid);
if (pgrp == NULL)
return (ESRCH);
}
lockmgr(&pgrp->pg_lock, LK_EXCLUSIVE);
LIST_FOREACH(p, &pgrp->pg_members, p_pglist) {
if (p->p_pid <= 1 ||
p->p_stat == SZOMB ||
(p->p_flags & P_SYSTEM) ||
!CANSIGNAL(p, sig, 0)) {
continue;
}
++info.nfound;
if (sig)
ksignal(p, sig);
}
lockmgr(&pgrp->pg_lock, LK_RELEASE);
pgrel(pgrp);
}
return (info.nfound ? 0 : ESRCH);
}
static int
killpg_all_callback(struct proc *p, void *data)
{
struct killpg_info *info = data;
if (p->p_pid <= 1 || (p->p_flags & P_SYSTEM) ||
p == curproc || !CANSIGNAL(p, info->sig, 0)) {
return (0);
}
++info->nfound;
if (info->sig)
ksignal(p, info->sig);
return(0);
}
int
kern_kill(int sig, pid_t pid, lwpid_t tid)
{
int t;
if ((u_int)sig >= _SIG_MAXSIG)
return (EINVAL);
if (pid > 0) {
struct proc *p;
struct lwp *lp = NULL;
if (pid == 1 && caps_priv_check_self(SYSCAP_NOREBOOT | __SYSCAP_WHEELOK))
return EPERM;
if ((p = pfind(pid)) == NULL) {
if ((p = zpfind(pid)) == NULL)
return (ESRCH);
PRELE(p);
return (0);
}
if (p != curproc) {
lwkt_gettoken_shared(&p->p_token);
if (!CANSIGNAL(p, sig, 1)) {
lwkt_reltoken(&p->p_token);
PRELE(p);
return (EPERM);
}
lwkt_reltoken(&p->p_token);
}
if (p->p_flags & P_WEXIT) {
PRELE(p);
return (0);
}
if (tid != -1) {
lwkt_gettoken_shared(&p->p_token);
lp = lwp_rb_tree_RB_LOOKUP(&p->p_lwp_tree, tid);
if (lp == NULL) {
lwkt_reltoken(&p->p_token);
PRELE(p);
return (ESRCH);
}
LWPHOLD(lp);
lwkt_reltoken(&p->p_token);
}
if (sig)
lwpsignal(p, lp, sig);
if (lp)
LWPRELE(lp);
PRELE(p);
return (0);
}
if (tid != -1)
return (EINVAL);
switch (pid) {
case -1:
t = (dokillpg(sig, 0, 1));
break;
case 0:
t = (dokillpg(sig, 0, 0));
break;
default:
t = (dokillpg(sig, -pid, 0));
break;
}
return t;
}
int
sys_kill(struct sysmsg *sysmsg, const struct kill_args *uap)
{
int error;
error = kern_kill(uap->signum, uap->pid, -1);
return (error);
}
int
sys_lwp_kill(struct sysmsg *sysmsg, const struct lwp_kill_args *uap)
{
int error;
pid_t pid = uap->pid;
if (uap->tid == -1)
return (EINVAL);
if (pid == -1)
pid = curproc->p_pid;
error = kern_kill(uap->signum, pid, uap->tid);
return (error);
}
void
gsignal(int pgid, int sig)
{
struct pgrp *pgrp;
if (pgid && (pgrp = pgfind(pgid)))
pgsignal(pgrp, sig, 0);
}
void
pgsignal(struct pgrp *pgrp, int sig, int checkctty)
{
struct proc *p;
if (pgrp) {
pgref(pgrp);
lockmgr(&pgrp->pg_lock, LK_EXCLUSIVE);
LIST_FOREACH(p, &pgrp->pg_members, p_pglist) {
if (checkctty == 0 || p->p_flags & P_CONTROLT)
ksignal(p, sig);
}
lockmgr(&pgrp->pg_lock, LK_RELEASE);
pgrel(pgrp);
}
}
void
trapsignal(struct lwp *lp, int sig, u_long code)
{
struct proc *p = lp->lwp_proc;
struct sigacts *ps = p->p_sigacts;
if (lp->lwp_vkernel && lp->lwp_vkernel->ve) {
struct trapframe *tf = lp->lwp_md.md_regs;
tf->tf_trapno = 0;
vkernel_trap(lp, tf);
}
if ((p->p_flags & P_TRACED) == 0 && SIGISMEMBER(p->p_sigcatch, sig) &&
!SIGISMEMBER(lp->lwp_sigmask, sig)) {
lp->lwp_ru.ru_nsignals++;
#ifdef KTRACE
if (KTRPOINT(lp->lwp_thread, KTR_PSIG))
ktrpsig(lp, sig, ps->ps_sigact[_SIG_IDX(sig)],
&lp->lwp_sigmask, code);
#endif
(*p->p_sysent->sv_sendsig)(ps->ps_sigact[_SIG_IDX(sig)], sig,
&lp->lwp_sigmask, code);
SIGSETOR(lp->lwp_sigmask, ps->ps_catchmask[_SIG_IDX(sig)]);
if (!SIGISMEMBER(ps->ps_signodefer, sig))
SIGADDSET(lp->lwp_sigmask, sig);
if (SIGISMEMBER(ps->ps_sigreset, sig)) {
SIGDELSET(p->p_sigcatch, sig);
if (sig != SIGCONT &&
sigprop(sig) & SA_IGNORE)
SIGADDSET(p->p_sigignore, sig);
ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL;
}
} else {
lp->lwp_code = code;
lp->lwp_sig = sig;
lwpsignal(p, lp, sig);
}
}
static struct lwp *
find_lwp_for_signal(struct proc *p, int sig)
{
struct lwp *lp;
struct lwp *run, *sleep, *stop;
lp = lwkt_preempted_proc();
if (lp != NULL && lp->lwp_proc == p) {
LWPHOLD(lp);
lwkt_gettoken(&lp->lwp_token);
if (!SIGISMEMBER(lp->lwp_sigmask, sig)) {
return (lp);
}
lwkt_reltoken(&lp->lwp_token);
LWPRELE(lp);
}
run = sleep = stop = NULL;
FOREACH_LWP_IN_PROC(lp, p) {
LWPHOLD(lp);
lwkt_gettoken(&lp->lwp_token);
if (SIGISMEMBER(lp->lwp_sigmask, sig)) {
lwkt_reltoken(&lp->lwp_token);
LWPRELE(lp);
continue;
}
switch (lp->lwp_stat) {
case LSRUN:
if (sleep) {
lwkt_token_swap();
lwkt_reltoken(&sleep->lwp_token);
LWPRELE(sleep);
sleep = NULL;
run = lp;
} else if (stop) {
lwkt_token_swap();
lwkt_reltoken(&stop->lwp_token);
LWPRELE(stop);
stop = NULL;
run = lp;
} else {
run = lp;
}
break;
case LSSLEEP:
if (lp->lwp_flags & LWP_SINTR) {
if (sleep) {
lwkt_reltoken(&lp->lwp_token);
LWPRELE(lp);
} else if (stop) {
lwkt_token_swap();
lwkt_reltoken(&stop->lwp_token);
LWPRELE(stop);
stop = NULL;
sleep = lp;
} else {
sleep = lp;
}
} else {
lwkt_reltoken(&lp->lwp_token);
LWPRELE(lp);
}
break;
case LSSTOP:
if (sleep) {
lwkt_reltoken(&lp->lwp_token);
LWPRELE(lp);
} else if (stop) {
lwkt_reltoken(&lp->lwp_token);
LWPRELE(lp);
} else {
stop = lp;
}
break;
}
if (run)
break;
}
if (run != NULL)
return (run);
else if (sleep != NULL)
return (sleep);
else
return (stop);
}
void
ksignal(struct proc *p, int sig)
{
lwpsignal(p, NULL, sig);
}
void
lwpsignal(struct proc *p, struct lwp *lp, int sig)
{
struct proc *q;
sig_t action;
int prop;
KASSERT(_SIG_VALID(sig), ("%s: invalid signal %d", __func__, sig));
KKASSERT(lp == NULL || lp->lwp_proc == p);
PHOLD(p);
if (lp) {
LWPHOLD(lp);
lwkt_gettoken(&lp->lwp_token);
} else {
lwkt_gettoken(&p->p_token);
if (p->p_flags & P_WEXIT)
goto out;
}
prop = sigprop(sig);
if ((p->p_flags & P_TRACED) || (p->p_stops & S_SIG)) {
action = SIG_DFL;
} else {
if (lp && (lp->lwp_mpflags & LWP_MP_WEXIT) && sig != SIGKILL)
goto out;
if (SIGISMEMBER(p->p_sigignore, sig)) {
KNOTE(&p->p_klist, NOTE_SIGNAL | sig);
goto out;
}
if (SIGISMEMBER(p->p_sigcatch, sig))
action = SIG_CATCH;
else
action = SIG_DFL;
}
if (prop & SA_CONT) {
lwkt_gettoken(&p->p_token);
SIG_STOPSIGMASK_ATOMIC(p->p_siglist);
lwkt_reltoken(&p->p_token);
}
if (prop & SA_STOP) {
if ((prop & SA_TTYSTOP) && p->p_pgrp->pg_jobc == 0 &&
action == SIG_DFL)
goto out;
lwkt_gettoken(&p->p_token);
SIG_CONTSIGMASK_ATOMIC(p->p_siglist);
p->p_flags &= ~P_CONTINUED;
lwkt_reltoken(&p->p_token);
}
if (p->p_stat == SSTOP) {
lwkt_gettoken(&p->p_token);
if (p->p_stat != SSTOP) {
lwkt_reltoken(&p->p_token);
goto not_stopped;
}
sigsetfrompid(curthread, p, sig);
if (lp) {
spin_lock(&lp->lwp_spin);
SIGADDSET(lp->lwp_siglist, sig);
spin_unlock(&lp->lwp_spin);
} else {
SIGADDSET_ATOMIC(p->p_siglist, sig);
}
if (p->p_flags & P_TRACED) {
lwkt_reltoken(&p->p_token);
goto out;
}
if (sig == SIGKILL) {
proc_unstop(p, SSTOP);
lwkt_reltoken(&p->p_token);
goto active_process;
}
if (prop & SA_CONT) {
q = p->p_pptr;
PHOLD(q);
lwkt_gettoken(&q->p_token);
p->p_flags |= P_CONTINUED;
wakeup(q);
if (action == SIG_DFL)
SIGDELSET_ATOMIC(p->p_siglist, sig);
proc_unstop(p, SSTOP);
lwkt_reltoken(&q->p_token);
PRELE(q);
lwkt_reltoken(&p->p_token);
if (action == SIG_CATCH)
goto active_process;
goto out;
}
if (prop & SA_STOP) {
if (lwkt_preempted_proc() == NULL ||
lwkt_preempted_proc()->lwp_proc != p) {
SIGDELSET_ATOMIC(p->p_siglist, sig);
}
}
lwkt_reltoken(&p->p_token);
goto out;
}
not_stopped:
;
active_process:
if (lp == NULL) {
sigirefs_hold(p);
lp = find_lwp_for_signal(p, sig);
if (lp) {
if (SIGISMEMBER(lp->lwp_sigmask, sig)) {
lwkt_reltoken(&lp->lwp_token);
LWPRELE(lp);
lp = NULL;
} else {
lwkt_token_swap();
lwkt_reltoken(&p->p_token);
sigirefs_drop(p);
}
}
}
if (lp == NULL) {
sigsetfrompid(curthread, p, sig);
KNOTE(&p->p_klist, NOTE_SIGNAL | sig);
SIGADDSET_ATOMIC(p->p_siglist, sig);
sigirefs_drop(p);
goto out;
}
if (p->p_nice > NZERO && action == SIG_DFL && (prop & SA_KILL) &&
(p->p_flags & P_TRACED) == 0) {
lwkt_gettoken(&p->p_token);
p->p_nice = NZERO;
lwkt_reltoken(&p->p_token);
}
if ((prop & SA_STOP) && action == SIG_DFL) {
lwkt_gettoken(&p->p_token);
if (p->p_flags & P_PPWAIT) {
sigsetfrompid(curthread, p, sig);
SIGADDSET_ATOMIC(p->p_siglist, sig);
lwkt_reltoken(&p->p_token);
goto out;
}
if ((p->p_flags & P_WEXIT) == 0) {
p->p_xstat = sig;
proc_stop(p, SSTOP);
}
lwkt_reltoken(&p->p_token);
goto out;
}
if ((prop & SA_CONT) && action == SIG_DFL)
goto out;
sigsetfrompid(curthread, p, sig);
spin_lock(&lp->lwp_spin);
SIGADDSET(lp->lwp_siglist, sig);
spin_unlock(&lp->lwp_spin);
lwp_signotify(lp);
out:
if (lp) {
lwkt_reltoken(&lp->lwp_token);
LWPRELE(lp);
} else {
lwkt_reltoken(&p->p_token);
}
PRELE(p);
}
static void
lwp_signotify(struct lwp *lp)
{
thread_t dtd;
ASSERT_LWKT_TOKEN_HELD(&lp->lwp_token);
dtd = lp->lwp_thread;
crit_enter();
if (lp == lwkt_preempted_proc()) {
signotify();
} else if (lp->lwp_flags & LWP_SINTR) {
if (dtd->td_gd == mycpu) {
setrunnable(lp);
} else {
LWPHOLD(lp);
if (lp->lwp_stat == LSSTOP)
lp->lwp_stat = LSSLEEP;
lwkt_send_ipiq(dtd->td_gd, lwp_signotify_remote, lp);
}
} else if (dtd->td_flags & TDF_SINTR) {
if (dtd->td_gd == mycpu) {
setrunnable(lp);
} else {
LWPHOLD(lp);
if (lp->lwp_stat == LSSTOP)
lp->lwp_stat = LSSLEEP;
lwkt_send_ipiq(dtd->td_gd, lwp_signotify_remote, lp);
}
} else {
if (dtd->td_gd != mycpu) {
LWPHOLD(lp);
lwkt_send_ipiq(dtd->td_gd, lwp_signotify_remote, lp);
}
}
crit_exit();
}
static void
lwp_signotify_remote(void *arg)
{
struct lwp *lp = arg;
thread_t td = lp->lwp_thread;
if (lp == lwkt_preempted_proc()) {
signotify();
LWPRELE(lp);
} else if (td->td_gd == mycpu) {
if ((lp->lwp_flags & LWP_SINTR) ||
(td->td_flags & TDF_SINTR)) {
lwkt_schedule(td);
}
LWPRELE(lp);
} else {
lwkt_send_ipiq(td->td_gd, lwp_signotify_remote, lp);
}
}
void
proc_stop(struct proc *p, int stat)
{
struct proc *q;
struct lwp *lp;
ASSERT_LWKT_TOKEN_HELD(&p->p_token);
if (stat == SCORE) {
if (p->p_stat == SCORE || p->p_stat == SZOMB)
return;
} else {
if (p->p_stat == SSTOP || p->p_stat == SCORE ||
p->p_stat == SZOMB) {
return;
}
}
p->p_stat = stat;
FOREACH_LWP_IN_PROC(lp, p) {
LWPHOLD(lp);
lwkt_gettoken(&lp->lwp_token);
switch (lp->lwp_stat) {
case LSSTOP:
break;
case LSSLEEP:
if ((lp->lwp_mpflags & LWP_MP_WSTOP) == 0) {
atomic_set_int(&lp->lwp_mpflags, LWP_MP_WSTOP);
++p->p_nstopped;
}
break;
case LSRUN:
lwp_signotify(lp);
break;
}
lwkt_reltoken(&lp->lwp_token);
LWPRELE(lp);
}
if (p->p_nstopped == p->p_nthreads) {
q = p->p_pptr;
PHOLD(q);
lwkt_gettoken(&q->p_token);
p->p_flags &= ~P_WAITED;
wakeup(q);
if ((q->p_sigacts->ps_flag & PS_NOCLDSTOP) == 0)
ksignal(p->p_pptr, SIGCHLD);
lwkt_reltoken(&q->p_token);
PRELE(q);
}
}
void
proc_unstop(struct proc *p, int stat)
{
struct lwp *lp;
ASSERT_LWKT_TOKEN_HELD(&p->p_token);
if (p->p_stat != stat)
return;
p->p_stat = SACTIVE;
FOREACH_LWP_IN_PROC(lp, p) {
LWPHOLD(lp);
lwkt_gettoken(&lp->lwp_token);
switch (lp->lwp_stat) {
case LSRUN:
if (bootverbose)
kprintf("proc_unstop: lwp %d/%d not sleeping\n",
p->p_pid, lp->lwp_tid);
break;
case LSSLEEP:
if (lp->lwp_mpflags & LWP_MP_WSTOP) {
atomic_clear_int(&lp->lwp_mpflags,
LWP_MP_WSTOP);
--p->p_nstopped;
} else {
if (bootverbose)
kprintf("proc_unstop: lwp %d/%d sleeping, not stopped\n",
p->p_pid, lp->lwp_tid);
}
case LSSTOP:
lwp_signotify(lp);
break;
}
lwkt_reltoken(&lp->lwp_token);
LWPRELE(lp);
}
wakeup(p);
}
static void
proc_stopwait(struct proc *p)
{
while ((p->p_stat == SSTOP || p->p_stat == SCORE) &&
p->p_nstopped < p->p_nthreads - 1) {
tsleep_interlock(&p->p_nstopped, 0);
if (p->p_nstopped < p->p_nthreads - 1) {
tsleep(&p->p_nstopped, PINTERLOCKED, "stopwt", hz);
}
}
}
static int
kern_sigtimedwait(sigset_t waitset, siginfo_t *info, struct timespec *timeout)
{
sigset_t savedmask, set;
struct timespec rts, ets, ts;
struct proc *p = curproc;
struct lwp *lp = curthread->td_lwp;
bool timevalid;
int error, sig, hz;
error = 0;
sig = 0;
SIG_CANTMASK(waitset);
savedmask = lp->lwp_sigmask;
timespecclear(&ets);
timevalid = false;
if (timeout != NULL) {
if (timeout->tv_sec >= 0 && timeout->tv_nsec >= 0 &&
timeout->tv_nsec < 1000000000) {
timevalid = true;
getnanouptime(&rts);
timespecadd(&rts, timeout, &ets);
}
}
for (;;) {
set = lwp_sigpend(lp);
SIGSETAND(set, waitset);
if ((sig = sig_ffs(&set)) != 0) {
SIGFILLSET(lp->lwp_sigmask);
SIGDELSET(lp->lwp_sigmask, sig);
SIG_CANTMASK(lp->lwp_sigmask);
sig = issignal(lp, 1, NULL);
if (sig == 0)
continue;
else
break;
}
if (error)
break;
if (timeout != NULL) {
if (!timevalid) {
error = EINVAL;
break;
}
getnanouptime(&rts);
if (timespeccmp(&rts, &ets, >=)) {
error = EAGAIN;
break;
}
timespecsub(&ets, &rts, &ts);
hz = tstohz_high(&ts);
} else {
hz = 0;
}
lp->lwp_sigmask = savedmask;
SIGSETNAND(lp->lwp_sigmask, waitset);
sigirefs_wait(p);
error = tsleep(&p->p_sigacts, PCATCH, "sigwt", hz);
if (timeout != NULL) {
if (error == ERESTART) {
error = EINTR;
} else if (error == EAGAIN) {
error = 0;
}
}
}
lp->lwp_sigmask = savedmask;
sigirefs_wait(p);
if (sig) {
error = 0;
bzero(info, sizeof(*info));
info->si_signo = sig;
spin_lock(&lp->lwp_spin);
lwp_delsig(lp, sig, 1);
spin_unlock(&lp->lwp_spin);
#ifdef KTRACE
if (KTRPOINT(lp->lwp_thread, KTR_PSIG)) {
struct sigacts *ps = p->p_sigacts;
sig_t action = ps->ps_sigact[_SIG_IDX(sig)];
ktrpsig(lp, sig, action,
((lp->lwp_flags & LWP_OLDMASK) ?
&lp->lwp_oldsigmask : &lp->lwp_sigmask),
0);
}
#endif
if (sig == SIGKILL) {
sigexit(lp, sig);
}
}
return (error);
}
int
sys_sigtimedwait(struct sysmsg *sysmsg, const struct sigtimedwait_args *uap)
{
struct timespec ts;
struct timespec *timeout;
sigset_t set;
siginfo_t info;
int error;
if (uap->timeout) {
error = copyin(uap->timeout, &ts, sizeof(ts));
if (error)
return (error);
timeout = &ts;
} else {
timeout = NULL;
}
error = copyin(uap->set, &set, sizeof(set));
if (error)
return (error);
error = kern_sigtimedwait(set, &info, timeout);
if (error)
return (error);
if (uap->info)
error = copyout(&info, uap->info, sizeof(info));
if (error) {
ksignal(curproc, info.si_signo);
} else {
sysmsg->sysmsg_result = info.si_signo;
}
return (error);
}
int
sys_sigwaitinfo(struct sysmsg *sysmsg, const struct sigwaitinfo_args *uap)
{
siginfo_t info;
sigset_t set;
int error;
error = copyin(uap->set, &set, sizeof(set));
if (error)
return (error);
error = kern_sigtimedwait(set, &info, NULL);
if (error)
return (error);
if (uap->info)
error = copyout(&info, uap->info, sizeof(info));
if (error) {
ksignal(curproc, info.si_signo);
} else {
sysmsg->sysmsg_result = info.si_signo;
}
return (error);
}
int
iscaught(struct lwp *lp)
{
struct proc *p = lp->lwp_proc;
int sig;
if (p) {
if ((sig = CURSIG(lp)) != 0) {
if (SIGISMEMBER(p->p_sigacts->ps_sigintr, sig))
return (EINTR);
return (ERESTART);
}
}
return(EWOULDBLOCK);
}
int
issignal(struct lwp *lp, int maytrace, int *ptokp)
{
struct proc *p = lp->lwp_proc;
sigset_t mask;
int sig, prop;
int haveptok;
for (;;) {
int traced = (p->p_flags & P_TRACED) || (p->p_stops & S_SIG);
haveptok = 0;
#if 0
if (STOPLWP(p, lp)) {
lwkt_gettoken(&p->p_token);
tstop();
lwkt_reltoken(&p->p_token);
}
#endif
mask = lwp_sigpend(lp);
SIGSETNAND(mask, lp->lwp_sigmask);
if (p->p_flags & P_PPWAIT)
SIG_STOPSIGMASK(mask);
SIG_CONDBLOCKALLSIGS(mask, lp);
if (SIGISEMPTY(mask))
return (0);
sig = sig_ffs(&mask);
if (SIGISMEMBER(p->p_siglist, sig)) {
haveptok = 1;
lwkt_gettoken(&p->p_token);
mask = lwp_sigpend(lp);
SIGSETNAND(mask, lp->lwp_sigmask);
if (p->p_flags & P_PPWAIT)
SIG_STOPSIGMASK(mask);
if (SIGISEMPTY(mask)) {
lwkt_reltoken(&p->p_token);
return (0);
}
sig = sig_ffs(&mask);
}
STOPEVENT(p, S_SIG, sig);
if (SIGISMEMBER(p->p_sigignore, sig) && (traced == 0)) {
spin_lock(&lp->lwp_spin);
lwp_delsig(lp, sig, haveptok);
spin_unlock(&lp->lwp_spin);
if (haveptok)
lwkt_reltoken(&p->p_token);
continue;
}
if (maytrace &&
(p->p_flags & P_TRACED) &&
(p->p_flags & P_PPWAIT) == 0) {
if (haveptok == 0) {
lwkt_gettoken(&p->p_token);
haveptok = 1;
}
p->p_xstat = sig;
proc_stop(p, SSTOP);
if (p->p_flags & P_TRACED)
tstop();
spin_lock(&lp->lwp_spin);
lwp_delsig(lp, sig, 1);
spin_unlock(&lp->lwp_spin);
sig = p->p_xstat;
if (sig == 0) {
lwkt_reltoken(&p->p_token);
continue;
}
SIGADDSET_ATOMIC(p->p_siglist, sig);
if (SIGISMEMBER(lp->lwp_sigmask, sig)) {
lwkt_reltoken(&p->p_token);
continue;
}
if ((p->p_flags & P_TRACED) == 0) {
lwkt_reltoken(&p->p_token);
continue;
}
}
prop = sigprop(sig);
switch ((intptr_t)p->p_sigacts->ps_sigact[_SIG_IDX(sig)]) {
case (intptr_t)SIG_DFL:
if (p->p_pid <= 1) {
#ifdef DIAGNOSTIC
kprintf("Process (pid %lu) got signal %d\n",
(u_long)p->p_pid, sig);
#endif
break;
}
if (prop & SA_CKPT) {
if (haveptok == 0) {
lwkt_gettoken(&p->p_token);
haveptok = 1;
}
checkpoint_signal_handler(lp);
break;
}
if (prop & SA_STOP) {
if (haveptok == 0) {
lwkt_gettoken(&p->p_token);
haveptok = 1;
}
if (p->p_flags & P_TRACED ||
(p->p_pgrp->pg_jobc == 0 &&
prop & SA_TTYSTOP))
break;
if ((p->p_flags & P_WEXIT) == 0) {
p->p_xstat = sig;
proc_stop(p, SSTOP);
#if 0
tstop();
#endif
}
break;
} else if (prop & SA_IGNORE) {
break;
} else {
if (ptokp)
*ptokp = haveptok;
else if (haveptok)
lwkt_reltoken(&p->p_token);
return (sig);
}
case (intptr_t)SIG_IGN:
if ((prop & SA_CONT) == 0 &&
(p->p_flags & P_TRACED) == 0)
kprintf("%s: should not hit signal %d!\n",
__func__, sig);
break;
default:
if (ptokp)
*ptokp = haveptok;
else if (haveptok)
lwkt_reltoken(&p->p_token);
return (sig);
}
spin_lock(&lp->lwp_spin);
lwp_delsig(lp, sig, haveptok);
spin_unlock(&lp->lwp_spin);
if (haveptok)
lwkt_reltoken(&p->p_token);
}
}
void
postsig(int sig, int haveptok)
{
struct lwp *lp = curthread->td_lwp;
struct proc *p = lp->lwp_proc;
struct sigacts *ps = p->p_sigacts;
sig_t action;
sigset_t returnmask;
int code;
KASSERT(_SIG_VALID(sig), ("%s: invalid signal %d", __func__, sig));
if (lp->lwp_vkernel && lp->lwp_vkernel->ve) {
struct trapframe *tf = lp->lwp_md.md_regs;
tf->tf_trapno = 0;
vkernel_trap(lp, tf);
}
KNOTE(&p->p_klist, NOTE_SIGNAL | sig);
spin_lock(&lp->lwp_spin);
lwp_delsig(lp, sig, haveptok);
spin_unlock(&lp->lwp_spin);
action = ps->ps_sigact[_SIG_IDX(sig)];
#ifdef KTRACE
if (KTRPOINT(lp->lwp_thread, KTR_PSIG)) {
ktrpsig(lp, sig, action,
((lp->lwp_flags & LWP_OLDMASK) ?
&lp->lwp_oldsigmask : &lp->lwp_sigmask),
0);
}
#endif
if (haveptok)
lwkt_reltoken(&p->p_token);
STOPEVENT(p, S_SIG, sig);
if (action == SIG_DFL) {
sigexit(lp, sig);
} else {
KASSERT(action != SIG_IGN && !SIGISMEMBER(lp->lwp_sigmask, sig),
("postsig action"));
if (SIGISMEMBER(ps->ps_sigreset, sig)) {
SIGDELSET(p->p_sigcatch, sig);
if (sig != SIGCONT &&
sigprop(sig) & SA_IGNORE)
SIGADDSET(p->p_sigignore, sig);
ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL;
}
if (lp->lwp_flags & LWP_OLDMASK) {
returnmask = lp->lwp_oldsigmask;
lp->lwp_flags &= ~LWP_OLDMASK;
} else {
returnmask = lp->lwp_sigmask;
}
SIGSETOR(lp->lwp_sigmask, ps->ps_catchmask[_SIG_IDX(sig)]);
if (!SIGISMEMBER(ps->ps_signodefer, sig))
SIGADDSET(lp->lwp_sigmask, sig);
lp->lwp_ru.ru_nsignals++;
if (lp->lwp_sig != sig) {
code = 0;
} else {
code = lp->lwp_code;
lp->lwp_code = 0;
lp->lwp_sig = 0;
}
(*p->p_sysent->sv_sendsig)(action, sig, &returnmask, code);
}
}
void
killproc(struct proc *p, char *why)
{
log(LOG_ERR, "pid %d (%s), uid %d, was killed: %s\n",
p->p_pid, p->p_comm,
p->p_ucred ? p->p_ucred->cr_uid : -1, why);
ksignal(p, SIGKILL);
}
void
sigexit(struct lwp *lp, int sig)
{
struct proc *p = lp->lwp_proc;
lwkt_gettoken(&p->p_token);
p->p_acflag |= AXSIG;
if (sigprop(sig) & SA_CORE) {
lp->lwp_sig = sig;
if (p->p_stat != SCORE) {
proc_stop(p, SCORE);
proc_stopwait(p);
if (coredump(lp, sig) == 0)
sig |= WCOREFLAG;
p->p_stat = SSTOP;
}
if (kern_logsigexit) {
log(LOG_INFO,
"pid %d (%s), uid %d: exited on signal %d%s\n",
p->p_pid, p->p_comm,
p->p_ucred ? p->p_ucred->cr_uid : -1,
sig &~ WCOREFLAG,
sig & WCOREFLAG ? " (core dumped)" : "");
if (kern_logsigexit > 1)
kprintf("DEBUG - waiting on kern.logsigexit\n");
while (kern_logsigexit > 1) {
tsleep(&kern_logsigexit, 0, "DEBUG", hz);
}
}
}
lwkt_reltoken(&p->p_token);
exit1(W_EXITCODE(0, sig));
}
static char corefilename[MAXPATHLEN+1] = "%N.core";
SYSCTL_STRING(_kern, OID_AUTO, corefile, CTLFLAG_RW, corefilename,
sizeof(corefilename), "process corefile name format string");
static char *
expand_name(const char *name, uid_t uid, pid_t pid)
{
char *temp;
char buf[11];
int i, l, n;
char *format = corefilename;
size_t namelen;
temp = kmalloc(MAXPATHLEN + 1, M_TEMP, M_NOWAIT);
if (temp == NULL)
return NULL;
namelen = strlen(name);
for (i = 0, n = 0; n < MAXPATHLEN && format[i]; i++) {
switch (format[i]) {
case '%':
i++;
switch (format[i]) {
case '%':
temp[n++] = '%';
break;
case 'N':
if ((n + namelen) > MAXPATHLEN) {
log(LOG_ERR,
"pid %d (%s), uid (%u): "
"Path `%s%s' is too long\n",
pid, name, uid, temp, name);
kfree(temp, M_TEMP);
return NULL;
}
memcpy(temp+n, name, namelen);
n += namelen;
break;
case 'P':
l = ksprintf(buf, "%u", pid);
if ((n + l) > MAXPATHLEN) {
log(LOG_ERR,
"pid %d (%s), uid (%u): "
"Path `%s%s' is too long\n",
pid, name, uid, temp, name);
kfree(temp, M_TEMP);
return NULL;
}
memcpy(temp+n, buf, l);
n += l;
break;
case 'U':
l = ksprintf(buf, "%u", uid);
if ((n + l) > MAXPATHLEN) {
log(LOG_ERR,
"pid %d (%s), uid (%u): "
"Path `%s%s' is too long\n",
pid, name, uid, temp, name);
kfree(temp, M_TEMP);
return NULL;
}
memcpy(temp+n, buf, l);
n += l;
break;
default:
log(LOG_ERR,
"Unknown format character %c in `%s'\n",
format[i], format);
}
break;
default:
temp[n++] = format[i];
}
}
temp[n] = '\0';
return temp;
}
static int
coredump(struct lwp *lp, int sig)
{
struct proc *p = lp->lwp_proc;
struct vnode *vp;
struct ucred *cred = p->p_ucred;
struct flock lf;
struct nlookupdata nd;
struct vattr vattr;
int error, error1;
char *name;
off_t limit;
STOPEVENT(p, S_CORE, 0);
if (((sugid_coredump == 0) && (p->p_flags & P_SUGID)) ||
do_coredump == 0)
{
return (EFAULT);
}
limit = p->p_rlimit[RLIMIT_CORE].rlim_cur;
if (limit == 0)
return EFBIG;
name = expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid);
if (name == NULL)
return (EINVAL);
error = nlookup_init(&nd, name, UIO_SYSSPACE, NLC_LOCKVP);
if (error == 0)
error = vn_open(&nd, NULL,
O_CREAT | FWRITE | O_NOFOLLOW,
S_IRUSR | S_IWUSR);
kfree(name, M_TEMP);
if (error) {
nlookup_done(&nd);
return (error);
}
vp = nd.nl_open_vp;
nd.nl_open_vp = NULL;
nlookup_done(&nd);
vn_unlock(vp);
lf.l_whence = SEEK_SET;
lf.l_start = 0;
lf.l_len = 0;
lf.l_type = F_WRLCK;
error = VOP_ADVLOCK(vp, (caddr_t)p, F_SETLK, &lf, 0);
if (error)
goto out2;
if (vp->v_type != VREG ||
VOP_GETATTR(vp, &vattr) || vattr.va_nlink != 1) {
error = EFAULT;
goto out1;
}
if (vattr.va_uid != p->p_ucred->cr_uid) {
error = EFAULT;
goto out1;
}
VATTR_NULL(&vattr);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
vattr.va_size = 0;
VOP_SETATTR(vp, &vattr, cred);
p->p_acflag |= ACORE;
vn_unlock(vp);
error = p->p_sysent->sv_coredump ?
p->p_sysent->sv_coredump(lp, sig, vp, limit) : ENOSYS;
out1:
lf.l_type = F_UNLCK;
VOP_ADVLOCK(vp, (caddr_t)p, F_UNLCK, &lf, 0);
out2:
error1 = vn_close(vp, FWRITE, NULL);
if (error == 0)
error = error1;
return (error);
}
int
sys_nosys(struct sysmsg *sysmsg, const struct nosys_args *args)
{
lwpsignal(curproc, curthread->td_lwp, SIGSYS);
return (EINVAL);
}
void
pgsigio(struct sigio *sigio, int sig, int checkctty)
{
if (sigio == NULL)
return;
if (sigio->sio_pgid > 0) {
if (CANSIGIO(sigio->sio_ruid, sigio->sio_ucred,
sigio->sio_proc))
ksignal(sigio->sio_proc, sig);
} else if (sigio->sio_pgid < 0) {
struct proc *p;
struct pgrp *pg = sigio->sio_pgrp;
pgref(pg);
lockmgr(&pg->pg_lock, LK_EXCLUSIVE);
LIST_FOREACH(p, &pg->pg_members, p_pglist) {
if (CANSIGIO(sigio->sio_ruid, sigio->sio_ucred, p) &&
(checkctty == 0 || (p->p_flags & P_CONTROLT)))
ksignal(p, sig);
}
lockmgr(&pg->pg_lock, LK_RELEASE);
pgrel(pg);
}
}
static int
filt_sigattach(struct knote *kn)
{
struct proc *p = curproc;
kn->kn_ptr.p_proc = p;
kn->kn_flags |= EV_CLEAR;
knote_insert(&p->p_klist, kn);
return (0);
}
static void
filt_sigdetach(struct knote *kn)
{
struct proc *p = kn->kn_ptr.p_proc;
knote_remove(&p->p_klist, kn);
}
static int
filt_signal(struct knote *kn, long hint)
{
if (hint & NOTE_SIGNAL) {
hint &= ~NOTE_SIGNAL;
if (kn->kn_id == hint)
kn->kn_data++;
}
return (kn->kn_data != 0);
}