+ /* XXX This is racy because we don't get the lock!!!! */
+
+ /*
+ * Wait collision; go to sleep and restart; used to maintain
+ * the single return for waited process guarantee.
+ */
+ if (p->p_listflag & P_LIST_WAITING) {
+ (void) msleep(&p->p_stat, proc_list_mlock,
+ PWAIT, "waitidcoll", 0);
+ goto loop1;
+ }
+ p->p_listflag |= P_LIST_WAITING; /* mark busy */
+
+ nfound++;
+
+ bzero(&siginfo, sizeof (siginfo));
+
+ switch (p->p_stat) {
+ case SZOMB: /* Exited */
+ if (!(uap->options & WEXITED))
+ break;
+ proc_list_unlock();
+#if CONFIG_MACF
+ if ((error = mac_proc_check_wait(q, p)) != 0)
+ goto out;
+#endif
+ siginfo.si_signo = SIGCHLD;
+ siginfo.si_pid = p->p_pid;
+ siginfo.si_status = WEXITSTATUS(p->p_xstat);
+ if (WIFSIGNALED(p->p_xstat)) {
+ siginfo.si_code = WCOREDUMP(p->p_xstat) ?
+ CLD_DUMPED : CLD_KILLED;
+ } else
+ siginfo.si_code = CLD_EXITED;
+
+ if ((error = copyoutsiginfo(&siginfo,
+ caller64, uap->infop)) != 0)
+ goto out;
+
+ /* Prevent other process for waiting for this event? */
+ if (!(uap->options & WNOWAIT)) {
+ (void) reap_child_locked(q, p, 0, 0, 0, 0);
+ return (0);
+ }
+ goto out;
+
+ case SSTOP: /* Stopped */
+ /*
+ * If we are not interested in stopped processes, then
+ * ignore this one.
+ */
+ if (!(uap->options & WSTOPPED))
+ break;
+
+ /*
+ * If someone has already waited it, we lost a race
+ * to be the one to return status.
+ */
+ if ((p->p_lflag & P_LWAITED) != 0)
+ break;
+ proc_list_unlock();
+#if CONFIG_MACF
+ if ((error = mac_proc_check_wait(q, p)) != 0)
+ goto out;
+#endif
+ siginfo.si_signo = SIGCHLD;
+ siginfo.si_pid = p->p_pid;
+ siginfo.si_status = p->p_xstat; /* signal number */
+ siginfo.si_code = CLD_STOPPED;
+
+ if ((error = copyoutsiginfo(&siginfo,
+ caller64, uap->infop)) != 0)
+ goto out;
+
+ /* Prevent other process for waiting for this event? */
+ if (!(uap->options & WNOWAIT)) {
+ proc_lock(p);
+ p->p_lflag |= P_LWAITED;
+ proc_unlock(p);
+ }
+ goto out;
+
+ default: /* All other states => Continued */
+ if (!(uap->options & WCONTINUED))
+ break;
+
+ /*
+ * If the flag isn't set, then this process has not
+ * been stopped and continued, or the status has
+ * already been reaped by another caller of waitid().
+ */
+ if ((p->p_flag & P_CONTINUED) == 0)
+ break;
+ proc_list_unlock();
+#if CONFIG_MACF
+ if ((error = mac_proc_check_wait(q, p)) != 0)
+ goto out;
+#endif
+ siginfo.si_signo = SIGCHLD;
+ siginfo.si_code = CLD_CONTINUED;
+ proc_lock(p);
+ siginfo.si_pid = p->p_contproc;
+ siginfo.si_status = p->p_xstat;
+ proc_unlock(p);
+
+ if ((error = copyoutsiginfo(&siginfo,
+ caller64, uap->infop)) != 0)
+ goto out;
+
+ /* Prevent other process for waiting for this event? */
+ if (!(uap->options & WNOWAIT)) {
+ OSBitAndAtomic(~((uint32_t)P_CONTINUED),
+ &p->p_flag);
+ }
+ goto out;
+ }
+ ASSERT_LCK_MTX_OWNED(proc_list_mlock);
+
+ /* Not a process we are interested in; go on to next child */
+
+ p->p_listflag &= ~P_LIST_WAITING;
+ wakeup(&p->p_stat);
+ }
+ ASSERT_LCK_MTX_OWNED(proc_list_mlock);
+
+ /* No child processes that could possibly satisfy the request? */
+
+ if (nfound == 0) {
+ proc_list_unlock();
+ return (ECHILD);
+ }
+
+ if (uap->options & WNOHANG) {
+ proc_list_unlock();
+#if CONFIG_MACF
+ if ((error = mac_proc_check_wait(q, p)) != 0)
+ return (error);
+#endif
+ /*
+ * The state of the siginfo structure in this case
+ * is undefined. Some implementations bzero it, some
+ * (like here) leave it untouched for efficiency.
+ *
+ * Thus the most portable check for "no matching pid with
+ * WNOHANG" is to store a zero into si_pid before
+ * invocation, then check for a non-zero value afterwards.
+ */
+ return (0);
+ }