summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/os/exit.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/os/exit.c')
-rw-r--r--usr/src/uts/common/os/exit.c1232
1 files changed, 1232 insertions, 0 deletions
diff --git a/usr/src/uts/common/os/exit.c b/usr/src/uts/common/os/exit.c
new file mode 100644
index 0000000000..64d27497c4
--- /dev/null
+++ b/usr/src/uts/common/os/exit.c
@@ -0,0 +1,1232 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* from SVr4.0 1.74 */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/cred.h>
+#include <sys/user.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/ucontext.h>
+#include <sys/procfs.h>
+#include <sys/vnode.h>
+#include <sys/acct.h>
+#include <sys/var.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/wait.h>
+#include <sys/siginfo.h>
+#include <sys/procset.h>
+#include <sys/class.h>
+#include <sys/file.h>
+#include <sys/session.h>
+#include <sys/kmem.h>
+#include <sys/vtrace.h>
+#include <sys/prsystm.h>
+#include <sys/ipc.h>
+#include <sys/sem_impl.h>
+#include <c2/audit.h>
+#include <sys/aio_impl.h>
+#include <vm/as.h>
+#include <sys/poll.h>
+#include <sys/door.h>
+#include <sys/lwpchan_impl.h>
+#include <sys/utrap.h>
+#include <sys/task.h>
+#include <sys/exacct.h>
+#include <sys/cyclic.h>
+#include <sys/schedctl.h>
+#include <sys/rctl.h>
+#include <sys/contract_impl.h>
+#include <sys/contract/process_impl.h>
+#include <sys/list.h>
+#include <sys/dtrace.h>
+#include <sys/pool.h>
+#include <sys/sdt.h>
+#include <sys/corectl.h>
+
+#if defined(__x86)
+extern void ldt_free(proc_t *pp);
+#endif
+
+/*
+ * convert code/data pair into old style wait status
+ */
+int
+wstat(int code, int data)
+{
+ int stat = (data & 0377);
+
+ switch (code) {
+ case CLD_EXITED:
+ stat <<= 8;
+ break;
+ case CLD_DUMPED:
+ stat |= WCOREFLG;
+ break;
+ case CLD_KILLED:
+ break;
+ case CLD_TRAPPED:
+ case CLD_STOPPED:
+ stat <<= 8;
+ stat |= WSTOPFLG;
+ break;
+ case CLD_CONTINUED:
+ stat = WCONTFLG;
+ break;
+ default:
+ cmn_err(CE_PANIC, "wstat: bad code");
+ /* NOTREACHED */
+ }
+ return (stat);
+}
+
+static char *
+exit_reason(char *buf, size_t bufsz, int what, int why)
+{
+ switch (why) {
+ case CLD_EXITED:
+ (void) snprintf(buf, bufsz, "exited with status %d", what);
+ break;
+ case CLD_KILLED:
+ (void) snprintf(buf, bufsz, "exited on fatal signal %d", what);
+ break;
+ case CLD_DUMPED:
+ (void) snprintf(buf, bufsz, "core dumped on signal %d", what);
+ break;
+ default:
+ (void) snprintf(buf, bufsz, "encountered unknown error "
+ "(%d, %d)", why, what);
+ break;
+ }
+
+ return (buf);
+}
+
+/*
+ * exit system call: pass back caller's arg.
+ */
+void
+rexit(int rval)
+{
+ exit(CLD_EXITED, rval);
+}
+
+/*
+ * Called by proc_exit() when a zone's init exits, presumably because
+ * it failed. As long as the given zone is still in the "running"
+ * state, we will re-exec() init, but first we need to reset things
+ * which are usually inherited across exec() but will break init's
+ * assumption that it is being exec()'d from a virgin process. Most
+ * importantly this includes closing all file descriptors (exec only
+ * closes those marked close-on-exec) and resetting signals (exec only
+ * resets handled signals, and we need to clear any signals which
+ * killed init). Anything else that exec(2) says would be inherited,
+ * but would affect the execution of init, needs to be reset.
+ */
+static int
+restart_init(int what, int why)
+{
+ kthread_t *t = curthread;
+ klwp_t *lwp = ttolwp(t);
+ proc_t *p = ttoproc(t);
+ user_t *up = PTOU(p);
+
+ vnode_t *oldcd, *oldrd;
+ sess_t *sp;
+ int i, err;
+ char reason_buf[64];
+ const char *ipath;
+
+ /*
+ * Let zone admin (and global zone admin if this is for a non-global
+ * zone) know that init has failed and will be restarted.
+ */
+ zcmn_err(p->p_zone->zone_id, CE_WARN,
+ "init(1M) %s: restarting automatically",
+ exit_reason(reason_buf, sizeof (reason_buf), what, why));
+
+ if (!INGLOBALZONE(p)) {
+ cmn_err(CE_WARN, "init(1M) for zone %s (pid %d) %s: "
+ "restarting automatically",
+ p->p_zone->zone_name, p->p_pid, reason_buf);
+ }
+
+ /*
+ * Remove any fpollinfo_t's for this (last) thread from our file
+ * descriptors so closeall() can ASSERT() that they're all gone.
+ * Then close all open file descriptors in the process.
+ */
+ pollcleanup();
+ closeall(P_FINFO(p));
+
+ /*
+ * Grab p_lock and begin clearing miscellaneous global process
+ * state that needs to be reset before we exec the new init(1M).
+ */
+
+ mutex_enter(&p->p_lock);
+
+ p->p_flag &= ~(SKILLED | SEXITLWPS | SEXTKILLED | SCOREDUMP | SDOCORE);
+ up->u_cmask = CMASK;
+
+ sigemptyset(&t->t_hold);
+ sigemptyset(&t->t_sig);
+ sigemptyset(&t->t_extsig);
+
+ sigemptyset(&p->p_sig);
+ sigemptyset(&p->p_extsig);
+
+ sigdelq(p, t, 0);
+ sigdelq(p, NULL, 0);
+
+ if (p->p_killsqp) {
+ siginfofree(p->p_killsqp);
+ p->p_killsqp = NULL;
+ }
+
+ /*
+ * Reset any signals that are ignored back to the default disposition.
+ * Other u_signal members will be cleared when exec calls sigdefault().
+ */
+ for (i = 1; i < NSIG; i++) {
+ if (up->u_signal[i - 1] == SIG_IGN) {
+ up->u_signal[i - 1] = SIG_DFL;
+ sigemptyset(&up->u_sigmask[i - 1]);
+ }
+ }
+
+ /*
+ * Clear the current signal, any signal info associated with it, and
+ * any signal information from contracts and/or contract templates.
+ */
+ lwp->lwp_cursig = 0;
+ lwp->lwp_extsig = 0;
+ if (lwp->lwp_curinfo != NULL) {
+ siginfofree(lwp->lwp_curinfo);
+ lwp->lwp_curinfo = NULL;
+ }
+ lwp_ctmpl_clear(lwp);
+
+ /*
+ * Reset both the process root directory and the current working
+ * directory to the root of the zone just as we do during boot.
+ */
+ VN_HOLD(p->p_zone->zone_rootvp);
+ oldrd = up->u_rdir;
+ up->u_rdir = p->p_zone->zone_rootvp;
+
+ VN_HOLD(p->p_zone->zone_rootvp);
+ oldcd = up->u_cdir;
+ up->u_cdir = p->p_zone->zone_rootvp;
+
+ if (up->u_cwd != NULL) {
+ refstr_rele(up->u_cwd);
+ up->u_cwd = NULL;
+ }
+
+ mutex_exit(&p->p_lock);
+
+ if (oldrd != NULL)
+ VN_RELE(oldrd);
+ if (oldcd != NULL)
+ VN_RELE(oldcd);
+
+ /*
+ * Free the controlling tty.
+ */
+ mutex_enter(&pidlock);
+ sp = p->p_sessp;
+ if (sp->s_sidp == p->p_pidp && sp->s_vp != NULL) {
+ mutex_exit(&pidlock);
+ freectty(sp);
+ } else {
+ mutex_exit(&pidlock);
+ }
+
+ /*
+ * Now exec() the new init(1M) on top of the current process. If we
+ * succeed, the caller will treat this like a successful system call.
+ * If we fail, we issue messages and the caller will proceed with exit.
+ */
+ ipath = INGLOBALZONE(p) ? initname : zone_initname;
+ err = exec_init(ipath, 0, NULL);
+
+ if (err == 0)
+ return (0);
+
+ zcmn_err(p->p_zone->zone_id, CE_WARN,
+ "failed to restart init(1M) (err=%d): system reboot required", err);
+
+ if (!INGLOBALZONE(p)) {
+ cmn_err(CE_WARN, "failed to restart init(1M) for zone %s "
+ "(pid %d, err=%d): zoneadm(1M) boot required",
+ p->p_zone->zone_name, p->p_pid, err);
+ }
+
+ return (-1);
+}
+
+/*
+ * Release resources.
+ * Enter zombie state.
+ * Wake up parent and init processes,
+ * and dispose of children.
+ */
+void
+exit(int why, int what)
+{
+ /*
+ * If proc_exit() fails, then some other lwp in the process
+ * got there first. We just have to call lwp_exit() to allow
+ * the other lwp to finish exiting the process. Otherwise we're
+ * restarting init, and should return.
+ */
+ if (proc_exit(why, what) != 0) {
+ mutex_enter(&curproc->p_lock);
+ ASSERT(curproc->p_flag & SEXITLWPS);
+ lwp_exit();
+ /* NOTREACHED */
+ }
+}
+
+/*
+ * Return value:
+ * 1 - exitlwps() failed, call (or continue) lwp_exit()
+ * 0 - restarting init. Return through system call path
+ */
+int
+proc_exit(int why, int what)
+{
+ kthread_t *t = curthread;
+ klwp_t *lwp = ttolwp(t);
+ proc_t *p = ttoproc(t);
+ zone_t *z = p->p_zone;
+ timeout_id_t tmp_id;
+ int rv;
+ proc_t *q;
+ sess_t *sp;
+ task_t *tk;
+ vnode_t *exec_vp, *execdir_vp, *cdir, *rdir;
+ sigqueue_t *sqp;
+ lwpdir_t *lwpdir;
+ uint_t lwpdir_sz;
+ lwpdir_t **tidhash;
+ uint_t tidhash_sz;
+ refstr_t *cwd;
+ hrtime_t hrutime, hrstime;
+
+ /*
+ * Stop and discard the process's lwps except for the current one,
+ * unless some other lwp beat us to it. If exitlwps() fails then
+ * return and the calling lwp will call (or continue in) lwp_exit().
+ */
+ if (exitlwps(0) != 0)
+ return (1);
+
+ DTRACE_PROC(lwp__exit);
+ DTRACE_PROC1(exit, int, why);
+
+ /*
+ * Don't let init exit unless zone_icode() failed its exec, or
+ * we are shutting down the zone or the machine.
+ *
+ * Since we are single threaded, we don't need to lock the
+ * following accesses to zone_proc_initpid.
+ */
+ if (p->p_pid == z->zone_proc_initpid) {
+ if (z->zone_boot_err == 0 &&
+ zone_status_get(z) < ZONE_IS_SHUTTING_DOWN &&
+ zone_status_get(global_zone) < ZONE_IS_SHUTTING_DOWN &&
+ restart_init(what, why) == 0)
+ return (0);
+ /*
+ * Since we didn't or couldn't restart init, we clear
+ * the zone's init state and proceed with exit
+ * processing.
+ */
+ z->zone_proc_initpid = -1;
+ }
+
+ /*
+ * Allocate a sigqueue now, before we grab locks.
+ * It will be given to sigcld(), below.
+ */
+ sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+
+ /*
+ * revoke any doors created by the process.
+ */
+ if (p->p_door_list)
+ door_exit();
+
+ /*
+ * Release schedctl data structures.
+ */
+ if (p->p_pagep)
+ schedctl_proc_cleanup();
+
+ /*
+ * make sure all pending kaio has completed.
+ */
+ if (p->p_aio)
+ aio_cleanup_exit();
+
+ /*
+ * discard the lwpchan cache.
+ */
+ if (p->p_lcp != NULL)
+ lwpchan_destroy_cache(0);
+
+ /*
+ * Clean up any DTrace helper actions or probes for the process.
+ */
+ if (p->p_dtrace_helpers != NULL) {
+ ASSERT(dtrace_helpers_cleanup != NULL);
+ (*dtrace_helpers_cleanup)();
+ }
+
+ /* untimeout the realtime timers */
+ if (p->p_itimer != NULL)
+ timer_exit();
+
+ if ((tmp_id = p->p_alarmid) != 0) {
+ p->p_alarmid = 0;
+ (void) untimeout(tmp_id);
+ }
+
+ /*
+ * Remove any fpollinfo_t's for this (last) thread from our file
+ * descriptors so closeall() can ASSERT() that they're all gone.
+ */
+ pollcleanup();
+
+ if (p->p_rprof_cyclic != CYCLIC_NONE) {
+ mutex_enter(&cpu_lock);
+ cyclic_remove(p->p_rprof_cyclic);
+ mutex_exit(&cpu_lock);
+ }
+
+ mutex_enter(&p->p_lock);
+
+ /*
+ * Clean up any DTrace probes associated with this process.
+ */
+ if (p->p_dtrace_probes) {
+ ASSERT(dtrace_fasttrap_exit_ptr != NULL);
+ dtrace_fasttrap_exit_ptr(p);
+ }
+
+ while ((tmp_id = p->p_itimerid) != 0) {
+ p->p_itimerid = 0;
+ mutex_exit(&p->p_lock);
+ (void) untimeout(tmp_id);
+ mutex_enter(&p->p_lock);
+ }
+
+ lwp_cleanup();
+
+ /*
+ * We are about to exit; prevent our resource associations from
+ * being changed.
+ */
+ pool_barrier_enter();
+
+ /*
+ * Block the process against /proc now that we have really
+ * acquired p->p_lock (to manipulate p_tlist at least).
+ */
+ prbarrier(p);
+
+#ifdef SUN_SRC_COMPAT
+ if (code == CLD_KILLED)
+ u.u_acflag |= AXSIG;
+#endif
+ sigfillset(&p->p_ignore);
+ sigemptyset(&p->p_siginfo);
+ sigemptyset(&p->p_sig);
+ sigemptyset(&p->p_extsig);
+ sigemptyset(&t->t_sig);
+ sigemptyset(&t->t_extsig);
+ sigemptyset(&p->p_sigmask);
+ sigdelq(p, t, 0);
+ lwp->lwp_cursig = 0;
+ lwp->lwp_extsig = 0;
+ p->p_flag &= ~(SKILLED | SEXTKILLED);
+ if (lwp->lwp_curinfo) {
+ siginfofree(lwp->lwp_curinfo);
+ lwp->lwp_curinfo = NULL;
+ }
+
+ t->t_proc_flag |= TP_LWPEXIT;
+ ASSERT(p->p_lwpcnt == 1 && p->p_zombcnt == 0);
+ prlwpexit(t); /* notify /proc */
+ lwp_hash_out(p, t->t_tid);
+ prexit(p);
+
+ p->p_lwpcnt = 0;
+ p->p_tlist = NULL;
+ sigqfree(p);
+ term_mstate(t);
+ p->p_mterm = gethrtime();
+
+ exec_vp = p->p_exec;
+ execdir_vp = p->p_execdir;
+ p->p_exec = NULLVP;
+ p->p_execdir = NULLVP;
+ mutex_exit(&p->p_lock);
+ if (exec_vp)
+ VN_RELE(exec_vp);
+ if (execdir_vp)
+ VN_RELE(execdir_vp);
+
+ pr_free_watched_pages(p);
+
+ closeall(P_FINFO(p));
+
+ mutex_enter(&pidlock);
+ sp = p->p_sessp;
+ if (sp->s_sidp == p->p_pidp && sp->s_vp != NULL) {
+ mutex_exit(&pidlock);
+ freectty(sp);
+ } else
+ mutex_exit(&pidlock);
+
+#if defined(__x86)
+ /*
+ * If the process was using a private LDT then free it.
+ */
+ if (p->p_ldt)
+ ldt_free(p);
+#endif
+
+#if defined(__sparc)
+ if (p->p_utraps != NULL)
+ utrap_free(p);
+#endif
+ if (p->p_semacct) /* IPC semaphore exit */
+ semexit(p);
+ rv = wstat(why, what);
+
+ acct(rv & 0xff);
+ exacct_commit_proc(p, rv);
+
+ /*
+ * Release any resources associated with C2 auditing
+ */
+#ifdef C2_AUDIT
+ if (audit_active) {
+ /*
+ * audit exit system call
+ */
+ audit_exit(why, what);
+ }
+#endif
+
+ /*
+ * Free address space.
+ */
+ relvm();
+
+ /*
+ * Release held contracts.
+ */
+ contract_exit(p);
+
+ /*
+ * Depart our encapsulating process contract.
+ */
+ if ((p->p_flag & SSYS) == 0) {
+ ASSERT(p->p_ct_process);
+ contract_process_exit(p->p_ct_process, p, rv);
+ }
+
+ /*
+ * Remove pool association, and block if requested by pool_do_bind.
+ */
+ mutex_enter(&p->p_lock);
+ ASSERT(p->p_pool->pool_ref > 0);
+ atomic_add_32(&p->p_pool->pool_ref, -1);
+ p->p_pool = pool_default;
+ /*
+ * Now that our address space has been freed and all other threads
+ * in this process have exited, set the PEXITED pool flag. This
+ * tells the pools subsystems to ignore this process if it was
+ * requested to rebind this process to a new pool.
+ */
+ p->p_poolflag |= PEXITED;
+ pool_barrier_exit();
+ mutex_exit(&p->p_lock);
+
+ mutex_enter(&pidlock);
+
+ /*
+ * Delete this process from the newstate list of its parent. We
+ * will put it in the right place in the sigcld in the end.
+ */
+ delete_ns(p->p_parent, p);
+
+ /*
+ * Reassign the orphans to the next of kin.
+ * Don't rearrange init's orphanage.
+ */
+ if ((q = p->p_orphan) != NULL && p != proc_init) {
+
+ proc_t *nokp = p->p_nextofkin;
+
+ for (;;) {
+ q->p_nextofkin = nokp;
+ if (q->p_nextorph == NULL)
+ break;
+ q = q->p_nextorph;
+ }
+ q->p_nextorph = nokp->p_orphan;
+ nokp->p_orphan = p->p_orphan;
+ p->p_orphan = NULL;
+ }
+
+ /*
+ * Reassign the children to init.
+ * Don't try to assign init's children to init.
+ */
+ if ((q = p->p_child) != NULL && p != proc_init) {
+ struct proc *np;
+ struct proc *initp = proc_init;
+ boolean_t setzonetop = B_FALSE;
+
+ if (!INGLOBALZONE(curproc))
+ setzonetop = B_TRUE;
+
+ pgdetach(p);
+
+ do {
+ np = q->p_sibling;
+ /*
+ * Delete it from its current parent new state
+ * list and add it to init new state list
+ */
+ delete_ns(q->p_parent, q);
+
+ q->p_ppid = 1;
+ if (setzonetop) {
+ mutex_enter(&q->p_lock);
+ q->p_flag |= SZONETOP;
+ mutex_exit(&q->p_lock);
+ }
+ q->p_parent = initp;
+
+ /*
+ * Since q will be the first child,
+ * it will not have a previous sibling.
+ */
+ q->p_psibling = NULL;
+ if (initp->p_child) {
+ initp->p_child->p_psibling = q;
+ }
+ q->p_sibling = initp->p_child;
+ initp->p_child = q;
+ if (q->p_proc_flag & P_PR_PTRACE) {
+ mutex_enter(&q->p_lock);
+ sigtoproc(q, NULL, SIGKILL);
+ mutex_exit(&q->p_lock);
+ }
+ /*
+ * sigcld() will add the child to parents
+ * newstate list.
+ */
+ if (q->p_stat == SZOMB)
+ sigcld(q, NULL);
+ } while ((q = np) != NULL);
+
+ p->p_child = NULL;
+ ASSERT(p->p_child_ns == NULL);
+ }
+
+ TRACE_1(TR_FAC_PROC, TR_PROC_EXIT, "proc_exit: %p", p);
+
+ mutex_enter(&p->p_lock);
+ CL_EXIT(curthread); /* tell the scheduler that curthread is exiting */
+
+ hrutime = mstate_aggr_state(p, LMS_USER);
+ hrstime = mstate_aggr_state(p, LMS_SYSTEM);
+ p->p_utime = (clock_t)NSEC_TO_TICK(hrutime) + p->p_cutime;
+ p->p_stime = (clock_t)NSEC_TO_TICK(hrstime) + p->p_cstime;
+
+ p->p_acct[LMS_USER] += p->p_cacct[LMS_USER];
+ p->p_acct[LMS_SYSTEM] += p->p_cacct[LMS_SYSTEM];
+ p->p_acct[LMS_TRAP] += p->p_cacct[LMS_TRAP];
+ p->p_acct[LMS_TFAULT] += p->p_cacct[LMS_TFAULT];
+ p->p_acct[LMS_DFAULT] += p->p_cacct[LMS_DFAULT];
+ p->p_acct[LMS_KFAULT] += p->p_cacct[LMS_KFAULT];
+ p->p_acct[LMS_USER_LOCK] += p->p_cacct[LMS_USER_LOCK];
+ p->p_acct[LMS_SLEEP] += p->p_cacct[LMS_SLEEP];
+ p->p_acct[LMS_WAIT_CPU] += p->p_cacct[LMS_WAIT_CPU];
+ p->p_acct[LMS_STOPPED] += p->p_cacct[LMS_STOPPED];
+
+ p->p_ru.minflt += p->p_cru.minflt;
+ p->p_ru.majflt += p->p_cru.majflt;
+ p->p_ru.nswap += p->p_cru.nswap;
+ p->p_ru.inblock += p->p_cru.inblock;
+ p->p_ru.oublock += p->p_cru.oublock;
+ p->p_ru.msgsnd += p->p_cru.msgsnd;
+ p->p_ru.msgrcv += p->p_cru.msgrcv;
+ p->p_ru.nsignals += p->p_cru.nsignals;
+ p->p_ru.nvcsw += p->p_cru.nvcsw;
+ p->p_ru.nivcsw += p->p_cru.nivcsw;
+ p->p_ru.sysc += p->p_cru.sysc;
+ p->p_ru.ioch += p->p_cru.ioch;
+
+ p->p_stat = SZOMB;
+ p->p_proc_flag &= ~P_PR_PTRACE;
+ p->p_wdata = what;
+ p->p_wcode = (char)why;
+
+ cdir = PTOU(p)->u_cdir;
+ rdir = PTOU(p)->u_rdir;
+ cwd = PTOU(p)->u_cwd;
+
+ /*
+ * Release resource controls, as they are no longer enforceable.
+ */
+ rctl_set_free(p->p_rctls);
+
+ /*
+ * Give up task and project memberships. Decrement tk_nlwps counter
+ * for our task.max-lwps resource control. An extended accounting
+ * record, if that facility is active, is scheduled to be written.
+ * Zombie processes are false members of task0 for the remainder of
+ * their lifetime; no accounting information is recorded for them.
+ */
+ tk = p->p_task;
+
+ mutex_enter(&p->p_zone->zone_nlwps_lock);
+ tk->tk_nlwps--;
+ tk->tk_proj->kpj_nlwps--;
+ p->p_zone->zone_nlwps--;
+ mutex_exit(&p->p_zone->zone_nlwps_lock);
+ task_detach(p);
+ p->p_task = task0p;
+
+ /*
+ * Clear the lwp directory and the lwpid hash table
+ * now that /proc can't bother us any more.
+ * We free the memory below, after dropping p->p_lock.
+ */
+ lwpdir = p->p_lwpdir;
+ lwpdir_sz = p->p_lwpdir_sz;
+ tidhash = p->p_tidhash;
+ tidhash_sz = p->p_tidhash_sz;
+ p->p_lwpdir = NULL;
+ p->p_lwpfree = NULL;
+ p->p_lwpdir_sz = 0;
+ p->p_tidhash = NULL;
+ p->p_tidhash_sz = 0;
+
+ /*
+ * curthread's proc pointer is changed to point at p0 because
+ * curthread's original proc pointer can be freed as soon as
+ * the child sends a SIGCLD to its parent.
+ */
+ t->t_procp = &p0;
+
+ mutex_exit(&p->p_lock);
+ sigcld(p, sqp);
+ mutex_exit(&pidlock);
+
+ task_rele(tk);
+
+ kmem_free(lwpdir, lwpdir_sz * sizeof (lwpdir_t));
+ kmem_free(tidhash, tidhash_sz * sizeof (lwpdir_t *));
+
+ /*
+ * We don't release u_cdir and u_rdir until SZOMB is set.
+ * This protects us against dofusers().
+ */
+ VN_RELE(cdir);
+ if (rdir)
+ VN_RELE(rdir);
+ if (cwd)
+ refstr_rele(cwd);
+
+ lwp_pcb_exit();
+
+ thread_exit();
+ /* NOTREACHED */
+}
+
+/*
+ * Format siginfo structure for wait system calls.
+ */
+void
+winfo(proc_t *pp, k_siginfo_t *ip, int waitflag)
+{
+ ASSERT(MUTEX_HELD(&pidlock));
+
+ bzero(ip, sizeof (k_siginfo_t));
+ ip->si_signo = SIGCLD;
+ ip->si_code = pp->p_wcode;
+ ip->si_pid = pp->p_pid;
+ ip->si_ctid = PRCTID(pp);
+ ip->si_zoneid = pp->p_zone->zone_id;
+ ip->si_status = pp->p_wdata;
+ ip->si_stime = pp->p_stime;
+ ip->si_utime = pp->p_utime;
+
+ if (waitflag) {
+ pp->p_wcode = 0;
+ pp->p_wdata = 0;
+ pp->p_pidflag &= ~CLDPEND;
+ }
+}
+
+/*
+ * Wait system call.
+ * Search for a terminated (zombie) child,
+ * finally lay it to rest, and collect its status.
+ * Look also for stopped children,
+ * and pass back status from them.
+ */
+int
+waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
+{
+ int found;
+ proc_t *cp, *pp;
+ proc_t **nsp;
+ int proc_gone;
+ int waitflag = !(options & WNOWAIT);
+
+ /*
+ * Obsolete flag, defined here only for binary compatibility
+ * with old statically linked executables. Delete this when
+ * we no longer care about these old and broken applications.
+ */
+#define _WNOCHLD 0400
+ options &= ~_WNOCHLD;
+
+ if (options == 0 || (options & ~WOPTMASK))
+ return (EINVAL);
+
+ switch (idtype) {
+ case P_PID:
+ case P_PGID:
+ if (id < 0 || id >= maxpid)
+ return (EINVAL);
+ /* FALLTHROUGH */
+ case P_ALL:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ pp = ttoproc(curthread);
+ /*
+ * lock parent mutex so that sibling chain can be searched.
+ */
+ mutex_enter(&pidlock);
+ while ((cp = pp->p_child) != NULL) {
+
+ proc_gone = 0;
+
+ for (nsp = &pp->p_child_ns; *nsp; nsp = &(*nsp)->p_sibling_ns) {
+ if (idtype == P_PID && id != (*nsp)->p_pid) {
+ continue;
+ }
+ if (idtype == P_PGID && id != (*nsp)->p_pgrp) {
+ continue;
+ }
+
+ switch ((*nsp)->p_wcode) {
+
+ case CLD_TRAPPED:
+ case CLD_STOPPED:
+ case CLD_CONTINUED:
+ cmn_err(CE_PANIC,
+ "waitid: wrong state %d on the p_newstate"
+ " list", (*nsp)->p_wcode);
+ break;
+
+ case CLD_EXITED:
+ case CLD_DUMPED:
+ case CLD_KILLED:
+ if (!(options & WEXITED)) {
+ /*
+ * Count how many are already gone
+ * for good.
+ */
+ proc_gone++;
+ break;
+ }
+ if (!waitflag) {
+ winfo((*nsp), ip, 0);
+ } else {
+ proc_t *xp = *nsp;
+ winfo(xp, ip, 1);
+ freeproc(xp);
+ }
+ mutex_exit(&pidlock);
+ if (waitflag) { /* accept SIGCLD */
+ sigcld_delete(ip);
+ sigcld_repost();
+ }
+ return (0);
+ }
+
+ if (idtype == P_PID)
+ break;
+ }
+
+ /*
+ * Wow! None of the threads on the p_sibling_ns list were
+ * interesting threads. Check all the kids!
+ */
+ found = 0;
+ cp = pp->p_child;
+ do {
+ if (idtype == P_PID && id != cp->p_pid) {
+ continue;
+ }
+ if (idtype == P_PGID && id != cp->p_pgrp) {
+ continue;
+ }
+
+ found++;
+
+ switch (cp->p_wcode) {
+ case CLD_TRAPPED:
+ if (!(options & WTRAPPED))
+ break;
+ winfo(cp, ip, waitflag);
+ mutex_exit(&pidlock);
+ if (waitflag) { /* accept SIGCLD */
+ sigcld_delete(ip);
+ sigcld_repost();
+ }
+ return (0);
+
+ case CLD_STOPPED:
+ if (!(options & WSTOPPED))
+ break;
+ /* Is it still stopped? */
+ mutex_enter(&cp->p_lock);
+ if (!jobstopped(cp)) {
+ mutex_exit(&cp->p_lock);
+ break;
+ }
+ mutex_exit(&cp->p_lock);
+ winfo(cp, ip, waitflag);
+ mutex_exit(&pidlock);
+ if (waitflag) { /* accept SIGCLD */
+ sigcld_delete(ip);
+ sigcld_repost();
+ }
+ return (0);
+
+ case CLD_CONTINUED:
+ if (!(options & WCONTINUED))
+ break;
+ winfo(cp, ip, waitflag);
+ mutex_exit(&pidlock);
+ if (waitflag) { /* accept SIGCLD */
+ sigcld_delete(ip);
+ sigcld_repost();
+ }
+ return (0);
+
+ case CLD_EXITED:
+ case CLD_DUMPED:
+ case CLD_KILLED:
+ /*
+ * Don't complain if a process was found in
+ * the first loop but we broke out of the loop
+ * because of the arguments passed to us.
+ */
+ if (proc_gone == 0) {
+ cmn_err(CE_PANIC,
+ "waitid: wrong state on the"
+ " p_child list");
+ } else {
+ break;
+ }
+ }
+
+ if (idtype == P_PID)
+ break;
+ } while ((cp = cp->p_sibling) != NULL);
+
+ /*
+ * If we found no interesting processes at all,
+ * break out and return ECHILD.
+ */
+ if (found + proc_gone == 0)
+ break;
+
+ if (options & WNOHANG) {
+ bzero(ip, sizeof (k_siginfo_t));
+ /*
+ * We should set ip->si_signo = SIGCLD,
+ * but there is an SVVS test that expects
+ * ip->si_signo to be zero in this case.
+ */
+ mutex_exit(&pidlock);
+ return (0);
+ }
+
+ /*
+ * If we found no processes of interest that could
+ * change state while we wait, we don't wait at all.
+ * Get out with ECHILD according to SVID.
+ */
+ if (found == proc_gone)
+ break;
+
+ if (!cv_wait_sig_swap(&pp->p_cv, &pidlock)) {
+ mutex_exit(&pidlock);
+ return (EINTR);
+ }
+ }
+ mutex_exit(&pidlock);
+ return (ECHILD);
+}
+
+/*
+ * For implementations that don't require binary compatibility,
+ * the wait system call may be made into a library call to the
+ * waitid system call.
+ */
+int64_t
+wait(void)
+{
+ int error;
+ k_siginfo_t info;
+ rval_t r;
+
+ if (error = waitid(P_ALL, (id_t)0, &info, WEXITED|WTRAPPED))
+ return (set_errno(error));
+ r.r_val1 = info.si_pid;
+ r.r_val2 = wstat(info.si_code, info.si_status);
+ return (r.r_vals);
+}
+
+int
+waitsys(idtype_t idtype, id_t id, siginfo_t *infop, int options)
+{
+ int error;
+ k_siginfo_t info;
+
+ if (error = waitid(idtype, id, &info, options))
+ return (set_errno(error));
+ if (copyout(&info, infop, sizeof (k_siginfo_t)))
+ return (set_errno(EFAULT));
+ return (0);
+}
+
+#ifdef _SYSCALL32_IMPL
+
+int
+waitsys32(idtype_t idtype, id_t id, siginfo_t *infop, int options)
+{
+ int error;
+ k_siginfo_t info;
+ siginfo32_t info32;
+
+ if (error = waitid(idtype, id, &info, options))
+ return (set_errno(error));
+ siginfo_kto32(&info, &info32);
+ if (copyout(&info32, infop, sizeof (info32)))
+ return (set_errno(EFAULT));
+ return (0);
+}
+
+#endif /* _SYSCALL32_IMPL */
+
+void
+proc_detach(proc_t *p)
+{
+ proc_t *q;
+
+ ASSERT(MUTEX_HELD(&pidlock));
+
+ q = p->p_parent;
+ ASSERT(q != NULL);
+
+ /*
+ * Take it off the newstate list of its parent
+ */
+ delete_ns(q, p);
+
+ if (q->p_child == p) {
+ q->p_child = p->p_sibling;
+ /*
+ * If the parent has no children, it better not
+ * have any with new states either!
+ */
+ ASSERT(q->p_child ? 1 : q->p_child_ns == NULL);
+ }
+
+ if (p->p_sibling) {
+ p->p_sibling->p_psibling = p->p_psibling;
+ }
+
+ if (p->p_psibling) {
+ p->p_psibling->p_sibling = p->p_sibling;
+ }
+}
+
+/*
+ * Remove zombie children from the process table.
+ */
+void
+freeproc(proc_t *p)
+{
+ proc_t *q;
+
+ ASSERT(p->p_stat == SZOMB);
+ ASSERT(p->p_tlist == NULL);
+ ASSERT(MUTEX_HELD(&pidlock));
+
+ sigdelq(p, NULL, 0);
+ if (p->p_killsqp) {
+ siginfofree(p->p_killsqp);
+ p->p_killsqp = NULL;
+ }
+
+ prfree(p); /* inform /proc */
+
+ /*
+ * Don't free the init processes.
+ * Other dying processes will access it.
+ */
+ if (p == proc_init)
+ return;
+
+
+ /*
+ * We wait until now to free the cred structure because a
+ * zombie process's credentials may be examined by /proc.
+ * No cred locking needed because there are no threads at this point.
+ */
+ upcount_dec(crgetruid(p->p_cred), crgetzoneid(p->p_cred));
+ crfree(p->p_cred);
+ if (p->p_corefile != NULL) {
+ corectl_path_rele(p->p_corefile);
+ p->p_corefile = NULL;
+ }
+ if (p->p_content != NULL) {
+ corectl_content_rele(p->p_content);
+ p->p_content = NULL;
+ }
+
+ if (p->p_nextofkin && !((p->p_nextofkin->p_flag & SNOWAIT) ||
+ (PTOU(p->p_nextofkin)->u_signal[SIGCLD - 1] == SIG_IGN))) {
+ /*
+ * This should still do the right thing since p_utime/stime
+ * get set to the correct value on process exit, so it
+ * should get properly updated
+ */
+ p->p_nextofkin->p_cutime += p->p_utime;
+ p->p_nextofkin->p_cstime += p->p_stime;
+
+ p->p_nextofkin->p_cacct[LMS_USER] += p->p_acct[LMS_USER];
+ p->p_nextofkin->p_cacct[LMS_SYSTEM] += p->p_acct[LMS_SYSTEM];
+ p->p_nextofkin->p_cacct[LMS_TRAP] += p->p_acct[LMS_TRAP];
+ p->p_nextofkin->p_cacct[LMS_TFAULT] += p->p_acct[LMS_TFAULT];
+ p->p_nextofkin->p_cacct[LMS_DFAULT] += p->p_acct[LMS_DFAULT];
+ p->p_nextofkin->p_cacct[LMS_KFAULT] += p->p_acct[LMS_KFAULT];
+ p->p_nextofkin->p_cacct[LMS_USER_LOCK]
+ += p->p_acct[LMS_USER_LOCK];
+ p->p_nextofkin->p_cacct[LMS_SLEEP] += p->p_acct[LMS_SLEEP];
+ p->p_nextofkin->p_cacct[LMS_WAIT_CPU]
+ += p->p_acct[LMS_WAIT_CPU];
+ p->p_nextofkin->p_cacct[LMS_STOPPED] += p->p_acct[LMS_STOPPED];
+
+ p->p_nextofkin->p_cru.minflt += p->p_ru.minflt;
+ p->p_nextofkin->p_cru.majflt += p->p_ru.majflt;
+ p->p_nextofkin->p_cru.nswap += p->p_ru.nswap;
+ p->p_nextofkin->p_cru.inblock += p->p_ru.inblock;
+ p->p_nextofkin->p_cru.oublock += p->p_ru.oublock;
+ p->p_nextofkin->p_cru.msgsnd += p->p_ru.msgsnd;
+ p->p_nextofkin->p_cru.msgrcv += p->p_ru.msgrcv;
+ p->p_nextofkin->p_cru.nsignals += p->p_ru.nsignals;
+ p->p_nextofkin->p_cru.nvcsw += p->p_ru.nvcsw;
+ p->p_nextofkin->p_cru.nivcsw += p->p_ru.nivcsw;
+ p->p_nextofkin->p_cru.sysc += p->p_ru.sysc;
+ p->p_nextofkin->p_cru.ioch += p->p_ru.ioch;
+
+ }
+
+ q = p->p_nextofkin;
+ if (q && q->p_orphan == p)
+ q->p_orphan = p->p_nextorph;
+ else if (q) {
+ for (q = q->p_orphan; q; q = q->p_nextorph)
+ if (q->p_nextorph == p)
+ break;
+ ASSERT(q && q->p_nextorph == p);
+ q->p_nextorph = p->p_nextorph;
+ }
+
+ proc_detach(p);
+ pid_exit(p); /* frees pid and proc structure */
+}
+
+/*
+ * Delete process "child" from the newstate list of process "parent"
+ */
+void
+delete_ns(proc_t *parent, proc_t *child)
+{
+ proc_t **ns;
+
+ ASSERT(MUTEX_HELD(&pidlock));
+ ASSERT(child->p_parent == parent);
+ for (ns = &parent->p_child_ns; *ns != NULL; ns = &(*ns)->p_sibling_ns) {
+ if (*ns == child) {
+
+ ASSERT((*ns)->p_parent == parent);
+
+ *ns = child->p_sibling_ns;
+ child->p_sibling_ns = NULL;
+ return;
+ }
+ }
+}
+
+/*
+ * Add process "child" to the new state list of process "parent"
+ */
+void
+add_ns(proc_t *parent, proc_t *child)
+{
+ ASSERT(child->p_sibling_ns == NULL);
+ child->p_sibling_ns = parent->p_child_ns;
+ parent->p_child_ns = child;
+}