#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/resourcevar.h>
#include <sys/ptrace.h>
#include <sys/acct.h>
#include <sys/filedesc.h>
#include <sys/signalvar.h>
#include <sys/sched.h>
#include <sys/ktrace.h>
#include <sys/pool.h>
#include <sys/mutex.h>
#ifdef SYSVSEM
#include <sys/sem.h>
#endif
#include <sys/witness.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <uvm/uvm_extern.h>
#include "kcov.h"
#if NKCOV > 0
#include <sys/kcov.h>
#endif
void proc_finish_wait(struct proc *, struct process *);
void process_clear_orphan(struct process *);
void process_zap(struct process *);
void proc_free(struct proc *);
void unveil_destroy(struct process *ps);
int
sys_exit(struct proc *p, void *v, register_t *retval)
{
struct sys_exit_args
*uap = v;
exit1(p, SCARG(uap, rval), 0, EXIT_NORMAL);
return (0);
}
int
sys___threxit(struct proc *p, void *v, register_t *retval)
{
struct sys___threxit_args
*uap = v;
if (SCARG(uap, notdead) != NULL) {
pid_t zero = 0;
if (copyout(&zero, SCARG(uap, notdead), sizeof(zero)))
psignal(p, SIGSEGV);
}
exit1(p, 0, 0, EXIT_THREAD);
return (0);
}
void
exit1(struct proc *p, int xexit, int xsig, int flags)
{
struct process *pr, *qr, *nqr;
struct rusage *rup;
atomic_setbits_int(&p->p_flag, P_WEXIT);
pr = p->p_p;
if (!P_HASSIBLING(p)) {
flags = EXIT_NORMAL;
} else {
if (flags == EXIT_NORMAL)
single_thread_set(p, SINGLE_EXIT);
}
if (flags == EXIT_NORMAL && !(pr->ps_flags & PS_EXITING)) {
if (pr->ps_pid == 1)
panic("init died (signal %d, exit %d)", xsig, xexit);
atomic_setbits_int(&pr->ps_flags, PS_EXITING);
pr->ps_xexit = xexit;
pr->ps_xsig = xsig;
if (pr->ps_flags & PS_PPWAIT) {
atomic_clearbits_int(&pr->ps_flags, PS_PPWAIT);
atomic_clearbits_int(&pr->ps_pptr->ps_flags,
PS_ISPWAIT);
atomic_setbits_int(&pr->ps_pptr->ps_flags,
PS_WAITEVENT);
wakeup(pr->ps_pptr);
}
refcnt_finalize(&pr->ps_refcnt, "psdtor");
}
mtx_enter(&pr->ps_mtx);
TAILQ_REMOVE(&pr->ps_threads, p, p_thr_link);
pr->ps_threadcnt--;
pr->ps_exitcnt++;
if (pr->ps_single != NULL || ISSET(pr->ps_flags, PS_STOPPING))
process_suspend_signal(pr);
tuagg_add_runtime();
tuagg_add_process(pr, p);
if ((p->p_flag & P_THREAD) == 0) {
while (pr->ps_threadcnt + pr->ps_exitcnt > 1)
msleep_nsec(&pr->ps_threads, &pr->ps_mtx, PWAIT,
"thrdeath", INFSLP);
}
mtx_leave(&pr->ps_mtx);
rup = pr->ps_ru;
if (rup == NULL) {
rup = pool_get(&rusage_pool, PR_WAITOK | PR_ZERO);
if (pr->ps_ru == NULL) {
pr->ps_ru = rup;
} else {
pool_put(&rusage_pool, rup);
rup = pr->ps_ru;
}
}
p->p_siglist = 0;
if ((p->p_flag & P_THREAD) == 0)
pr->ps_siglist = 0;
kqpoll_exit();
#if NKCOV > 0
kcov_exit(p);
#endif
if ((p->p_flag & P_THREAD) == 0) {
if (pr->ps_flags & PS_PROFIL)
stopprofclock(pr);
prof_write(p);
sigio_freelist(&pr->ps_sigiolst);
fdfree(p);
cancel_all_itimers();
timeout_del(&pr->ps_rucheck_to);
#ifdef SYSVSEM
semexit(pr);
#endif
killjobc(pr);
#ifdef ACCOUNTING
acct_process(p);
#endif
#ifdef KTRACE
if (pr->ps_tracevp)
ktrcleartrace(pr);
#endif
unveil_destroy(pr);
free(pr->ps_pin.pn_pins, M_PINSYSCALL,
pr->ps_pin.pn_npins * sizeof(u_int));
free(pr->ps_libcpin.pn_pins, M_PINSYSCALL,
pr->ps_libcpin.pn_npins * sizeof(u_int));
if (pr->ps_pptr->ps_sigacts->ps_sigflags & SAS_NOCLDWAIT)
atomic_setbits_int(&pr->ps_flags, PS_NOZOMBIE);
if ((p->p_flag & P_SYSTEM) == 0) {
#ifdef MULTIPROCESSOR
__mp_release_all(&kernel_lock);
#endif
uvm_purge();
KERNEL_LOCK();
}
}
p->p_fd = NULL;
if (p->p_limit != NULL) {
struct plimit *limit;
limit = p->p_limit;
p->p_limit = NULL;
lim_free(limit);
}
p->p_stat = SDEAD;
LIST_REMOVE(p, p_hash);
LIST_REMOVE(p, p_list);
if ((p->p_flag & P_THREAD) == 0) {
LIST_REMOVE(pr, ps_hash);
LIST_REMOVE(pr, ps_list);
if ((pr->ps_flags & PS_NOZOMBIE) == 0)
LIST_INSERT_HEAD(&zombprocess, pr, ps_list);
else {
freepid(pr->ps_pid);
}
qr = LIST_FIRST(&pr->ps_children);
if (qr)
wakeup(initprocess);
for (; qr != NULL; qr = nqr) {
nqr = LIST_NEXT(qr, ps_sibling);
mtx_enter(&qr->ps_mtx);
if (qr->ps_flags & PS_TRACED &&
!(qr->ps_flags & PS_EXITING)) {
process_untrace(qr);
mtx_leave(&qr->ps_mtx);
if (qr->ps_single)
ptsignal(qr->ps_single, SIGKILL,
STHREAD);
else
prsignal(qr, SIGKILL);
} else {
process_reparent(qr, initprocess);
mtx_leave(&qr->ps_mtx);
}
}
while ((qr = LIST_FIRST(&pr->ps_orphans)) != NULL) {
mtx_enter(&qr->ps_mtx);
KASSERT(qr->ps_opptr == pr);
qr->ps_opptr = NULL;
process_clear_orphan(qr);
mtx_leave(&qr->ps_mtx);
}
}
ruadd(rup, &p->p_ru);
p->p_pctcpu = 0;
if ((p->p_flag & P_THREAD) == 0) {
calcru(&pr->ps_tu, &rup->ru_utime, &rup->ru_stime, NULL);
rup->ru_ixrss = pr->ps_tu.tu_ixrss;
rup->ru_idrss = pr->ps_tu.tu_idrss;
rup->ru_isrss = pr->ps_tu.tu_isrss;
ruadd(rup, &pr->ps_cru);
mtx_enter(&pr->ps_mtx);
if (pr->ps_flags & PS_NOZOMBIE) {
struct process *ppr = pr->ps_pptr;
process_reparent(pr, initprocess);
atomic_setbits_int(&ppr->ps_flags, PS_WAITEVENT);
wakeup(ppr);
}
mtx_leave(&pr->ps_mtx);
}
if (p->p_flag & P_THREAD) {
mtx_enter(&pr->ps_mtx);
pr->ps_exitcnt--;
if (pr->ps_threadcnt + pr->ps_exitcnt == 1)
wakeup(&pr->ps_threads);
mtx_leave(&pr->ps_mtx);
}
cpu_exit(p);
pmap_deactivate(p);
sched_exit(p);
panic("sched_exit returned");
}
struct mutex deadproc_mutex =
MUTEX_INITIALIZER_FLAGS(IPL_NONE, "deadproc", MTX_NOWITNESS);
struct prochead deadproc = TAILQ_HEAD_INITIALIZER(deadproc);
void
exit2(struct proc *p)
{
mtx_enter(&p->p_p->ps_mtx);
tuagg_add_process(p->p_p, p);
mtx_leave(&p->p_p->ps_mtx);
mtx_enter(&deadproc_mutex);
TAILQ_INSERT_TAIL(&deadproc, p, p_runq);
mtx_leave(&deadproc_mutex);
wakeup(&deadproc);
}
void
proc_free(struct proc *p)
{
crfree(p->p_ucred);
pool_put(&proc_pool, p);
atomic_dec_int(&nthreads);
}
void
reaper(void *arg)
{
struct proc *p;
KERNEL_UNLOCK();
SCHED_ASSERT_UNLOCKED();
for (;;) {
mtx_enter(&deadproc_mutex);
while ((p = TAILQ_FIRST(&deadproc)) == NULL)
msleep_nsec(&deadproc, &deadproc_mutex, PVM, "reaper",
INFSLP);
TAILQ_REMOVE(&deadproc, p, p_runq);
mtx_leave(&deadproc_mutex);
WITNESS_THREAD_EXIT(p);
uvm_uarea_free(p);
p->p_vmspace = NULL;
if (p->p_flag & P_THREAD) {
proc_free(p);
} else {
struct process *pr = p->p_p;
uvm_exit(pr);
KERNEL_LOCK();
if ((pr->ps_flags & PS_NOZOMBIE) == 0) {
atomic_setbits_int(&pr->ps_flags, PS_ZOMBIE);
}
knote_processexit(pr);
if (pr->ps_flags & PS_ZOMBIE) {
prsignal(pr->ps_pptr, SIGCHLD);
atomic_setbits_int(&pr->ps_pptr->ps_flags,
PS_WAITEVENT);
wakeup(pr->ps_pptr);
} else {
process_zap(pr);
}
KERNEL_UNLOCK();
}
}
}
int
dowait6(struct proc *q, idtype_t idtype, id_t id, int *statusp, int options,
struct rusage *rusage, siginfo_t *info, register_t *retval)
{
int nfound;
struct process *pr;
int error;
if (info != NULL)
memset(info, 0, sizeof(*info));
loop:
atomic_clearbits_int(&q->p_p->ps_flags, PS_WAITEVENT);
nfound = 0;
LIST_FOREACH(pr, &q->p_p->ps_children, ps_sibling) {
mtx_enter(&pr->ps_mtx);
if ((pr->ps_flags & PS_NOZOMBIE) ||
(idtype == P_PID && id != pr->ps_pid) ||
(idtype == P_PGID && id != pr->ps_pgid)) {
mtx_leave(&pr->ps_mtx);
continue;
}
nfound++;
if ((options & WEXITED) && (pr->ps_flags & PS_ZOMBIE)) {
*retval = pr->ps_pid;
if (info != NULL) {
info->si_pid = pr->ps_pid;
info->si_uid = pr->ps_ucred->cr_uid;
info->si_signo = SIGCHLD;
if (pr->ps_xsig == 0) {
info->si_code = CLD_EXITED;
info->si_status = pr->ps_xexit;
} else if (WCOREDUMP(pr->ps_xsig)) {
info->si_code = CLD_DUMPED;
info->si_status = _WSTATUS(pr->ps_xsig);
} else {
info->si_code = CLD_KILLED;
info->si_status = _WSTATUS(pr->ps_xsig);
}
}
if (statusp != NULL)
*statusp = W_EXITCODE(pr->ps_xexit,
pr->ps_xsig);
if (rusage != NULL)
memcpy(rusage, pr->ps_ru, sizeof(*rusage));
mtx_leave(&pr->ps_mtx);
if ((options & WNOWAIT) == 0)
proc_finish_wait(q, pr);
return (0);
}
if ((options & WTRAPPED) && (pr->ps_flags & PS_TRACED) &&
(pr->ps_flags & PS_WAITED) == 0 &&
(pr->ps_flags & PS_STOPPED) &&
(pr->ps_flags & PS_TRAPPED)) {
if ((options & WNOWAIT) == 0)
atomic_setbits_int(&pr->ps_flags, PS_WAITED);
*retval = pr->ps_pid;
if (info != NULL) {
info->si_pid = pr->ps_pid;
info->si_uid = pr->ps_ucred->cr_uid;
info->si_signo = SIGCHLD;
info->si_code = CLD_TRAPPED;
info->si_status = pr->ps_xsig;
}
if (statusp != NULL)
*statusp = W_STOPCODE(pr->ps_xsig);
mtx_leave(&pr->ps_mtx);
if (rusage != NULL)
memset(rusage, 0, sizeof(*rusage));
return (0);
}
if (((pr->ps_flags & PS_TRACED) || (options & WUNTRACED)) &&
(pr->ps_flags & PS_WAITED) == 0 &&
(pr->ps_flags & PS_STOPPED) &&
(pr->ps_flags & PS_TRAPPED) == 0) {
if ((options & WNOWAIT) == 0)
atomic_setbits_int(&pr->ps_flags, PS_WAITED);
*retval = pr->ps_pid;
if (info != 0) {
info->si_pid = pr->ps_pid;
info->si_uid = pr->ps_ucred->cr_uid;
info->si_signo = SIGCHLD;
info->si_code = CLD_STOPPED;
info->si_status = pr->ps_xsig;
}
if (statusp != NULL)
*statusp = W_STOPCODE(pr->ps_xsig);
mtx_leave(&pr->ps_mtx);
if (rusage != NULL)
memset(rusage, 0, sizeof(*rusage));
return (0);
}
if ((options & WCONTINUED) && (pr->ps_flags & PS_CONTINUED)) {
if ((options & WNOWAIT) == 0)
atomic_clearbits_int(&pr->ps_flags,
PS_CONTINUED);
*retval = pr->ps_pid;
if (info != NULL) {
info->si_pid = pr->ps_pid;
info->si_uid = pr->ps_ucred->cr_uid;
info->si_signo = SIGCHLD;
info->si_code = CLD_CONTINUED;
info->si_status = SIGCONT;
}
mtx_leave(&pr->ps_mtx);
if (statusp != NULL)
*statusp = _WCONTINUED;
if (rusage != NULL)
memset(rusage, 0, sizeof(*rusage));
return (0);
}
mtx_leave(&pr->ps_mtx);
}
if (nfound == 0) {
LIST_FOREACH(pr, &q->p_p->ps_orphans, ps_orphan) {
if ((pr->ps_flags & PS_NOZOMBIE) ||
(idtype == P_PID && id != pr->ps_pid) ||
(idtype == P_PGID && id != pr->ps_pgid))
continue;
nfound++;
break;
}
}
if (nfound == 0)
return (ECHILD);
if (options & WNOHANG) {
*retval = 0;
return (0);
}
sleep_setup(q->p_p, PWAIT | PCATCH, "wait");
if ((error = sleep_finish(INFSLP,
!ISSET(atomic_load_int(&q->p_p->ps_flags), PS_WAITEVENT))) != 0)
return (error);
goto loop;
}
int
sys_wait4(struct proc *q, void *v, register_t *retval)
{
struct sys_wait4_args
*uap = v;
struct rusage ru;
pid_t pid = SCARG(uap, pid);
int options = SCARG(uap, options);
int status, error;
idtype_t idtype;
id_t id;
if (SCARG(uap, options) &~ (WUNTRACED|WNOHANG|WCONTINUED))
return (EINVAL);
options |= WEXITED | WTRAPPED;
if (SCARG(uap, pid) == WAIT_MYPGRP) {
idtype = P_PGID;
id = q->p_p->ps_pgid;
} else if (SCARG(uap, pid) == WAIT_ANY) {
idtype = P_ALL;
id = 0;
} else if (pid < 0) {
idtype = P_PGID;
id = -pid;
} else {
idtype = P_PID;
id = pid;
}
error = dowait6(q, idtype, id,
SCARG(uap, status) ? &status : NULL, options,
SCARG(uap, rusage) ? &ru : NULL, NULL, retval);
if (error == 0 && *retval > 0 && SCARG(uap, status)) {
error = copyout(&status, SCARG(uap, status), sizeof(status));
}
if (error == 0 && *retval > 0 && SCARG(uap, rusage)) {
error = copyout(&ru, SCARG(uap, rusage), sizeof(ru));
#ifdef KTRACE
if (error == 0 && KTRPOINT(q, KTR_STRUCT))
ktrrusage(q, &ru);
#endif
}
return (error);
}
int
sys_waitid(struct proc *q, void *v, register_t *retval)
{
struct sys_waitid_args
*uap = v;
siginfo_t info;
idtype_t idtype = SCARG(uap, idtype);
int options = SCARG(uap, options);
int error;
if (options &~ (WSTOPPED|WCONTINUED|WEXITED|WTRAPPED|WNOHANG|WNOWAIT))
return (EINVAL);
if ((options & (WSTOPPED|WCONTINUED|WEXITED|WTRAPPED)) == 0)
return (EINVAL);
if (idtype != P_ALL && idtype != P_PID && idtype != P_PGID)
return (EINVAL);
error = dowait6(q, idtype, SCARG(uap, id), NULL,
options, NULL, &info, retval);
if (error == 0) {
error = copyout(&info, SCARG(uap, info), sizeof(info));
#ifdef KTRACE
if (error == 0 && KTRPOINT(q, KTR_STRUCT))
ktrsiginfo(q, &info);
#endif
}
if (error == 0)
*retval = 0;
return (error);
}
void
proc_finish_wait(struct proc *waiter, struct process *pr)
{
struct process *tr;
struct rusage *rup;
mtx_enter(&pr->ps_mtx);
if (pr->ps_opptr != NULL && (pr->ps_opptr != pr->ps_pptr)) {
tr = pr->ps_opptr;
pr->ps_opptr = NULL;
atomic_clearbits_int(&pr->ps_flags, PS_TRACED);
process_reparent(pr, tr);
mtx_leave(&pr->ps_mtx);
prsignal(tr, SIGCHLD);
atomic_setbits_int(&tr->ps_flags, PS_WAITEVENT);
wakeup(tr);
} else {
mtx_leave(&pr->ps_mtx);
scheduler_wait_hook(waiter, pr->ps_mainproc);
rup = &waiter->p_p->ps_cru;
ruadd(rup, pr->ps_ru);
LIST_REMOVE(pr, ps_list);
freepid(pr->ps_pid);
process_zap(pr);
}
}
void
process_untrace(struct process *pr)
{
struct process *ppr = NULL;
KASSERT(pr->ps_flags & PS_TRACED);
MUTEX_ASSERT_LOCKED(&pr->ps_mtx);
if (pr->ps_opptr != NULL &&
(pr->ps_opptr != pr->ps_pptr))
ppr = pr->ps_opptr;
pr->ps_opptr = NULL;
atomic_clearbits_int(&pr->ps_flags, PS_TRACED);
process_reparent(pr, ppr ? ppr : initprocess);
}
void
process_clear_orphan(struct process *pr)
{
if (pr->ps_flags & PS_ORPHAN) {
LIST_REMOVE(pr, ps_orphan);
atomic_clearbits_int(&pr->ps_flags, PS_ORPHAN);
}
}
void
process_reparent(struct process *child, struct process *parent)
{
if (child->ps_pptr == parent)
return;
KASSERT(child->ps_opptr == NULL ||
child->ps_opptr == child->ps_pptr);
LIST_REMOVE(child, ps_sibling);
LIST_INSERT_HEAD(&parent->ps_children, child, ps_sibling);
process_clear_orphan(child);
if (child->ps_flags & PS_TRACED) {
atomic_setbits_int(&child->ps_flags, PS_ORPHAN);
LIST_INSERT_HEAD(&child->ps_pptr->ps_orphans, child, ps_orphan);
}
MUTEX_ASSERT_LOCKED(&child->ps_mtx);
child->ps_pptr = parent;
child->ps_ppid = parent->ps_pid;
WITNESS_SETCHILD(&child->ps_mtx.mtx_lock_obj,
&parent->ps_mtx.mtx_lock_obj);
}
void
process_zap(struct process *pr)
{
struct vnode *otvp;
struct proc *p = pr->ps_mainproc;
leavepgrp(pr);
LIST_REMOVE(pr, ps_sibling);
process_clear_orphan(pr);
(void)chgproccnt(pr->ps_ucred->cr_ruid, -1);
otvp = pr->ps_textvp;
pr->ps_textvp = NULL;
if (otvp)
vrele(otvp);
KASSERT(pr->ps_threadcnt == 0);
KASSERT(pr->ps_exitcnt == 1);
if (pr->ps_ptstat != NULL)
free(pr->ps_ptstat, M_SUBPROC, sizeof(*pr->ps_ptstat));
pool_put(&rusage_pool, pr->ps_ru);
KASSERT(TAILQ_EMPTY(&pr->ps_threads));
sigactsfree(pr->ps_sigacts);
lim_free(pr->ps_limit);
crfree(pr->ps_ucred);
pool_put(&process_pool, pr);
nprocesses--;
proc_free(p);
}