diff --git a/sys/bsm/audit_kevents.h b/sys/bsm/audit_kevents.h index f4f77a55163b..3eb2e3abb7d7 100644 --- a/sys/bsm/audit_kevents.h +++ b/sys/bsm/audit_kevents.h @@ -602,6 +602,7 @@ #define AUE_PDKILL 43198 /* FreeBSD. */ #define AUE_PDGETPID 43199 /* FreeBSD. */ #define AUE_PDWAIT 43200 /* FreeBSD. */ +#define AUE_WAIT6 43201 /* FreeBSD. */ /* * Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/procset.h b/sys/cddl/contrib/opensolaris/uts/common/sys/procset.h index c367c93966a1..8c5739b3bc28 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/sys/procset.h +++ b/sys/cddl/contrib/opensolaris/uts/common/sys/procset.h @@ -51,6 +51,7 @@ extern "C" { #define P_INITUID 0 #define P_INITPGID 0 +#ifndef _IDTYPE_T_DECLARED /* * The following defines the values for an identifier type. It @@ -81,6 +82,9 @@ typedef enum P_PSETID /* Processor set identifier */ } idtype_t; +#define _IDTYPE_T_DECLARED + +#endif /* * The following defines the operations which can be performed to diff --git a/sys/compat/freebsd32/freebsd32.h b/sys/compat/freebsd32/freebsd32.h index fccdef039f69..a95b0e53e04a 100644 --- a/sys/compat/freebsd32/freebsd32.h +++ b/sys/compat/freebsd32/freebsd32.h @@ -88,6 +88,11 @@ struct rusage32 { int32_t ru_nivcsw; }; +struct wrusage32 { + struct rusage32 wru_self; + struct rusage32 wru_children; +}; + struct itimerval32 { struct timeval32 it_interval; struct timeval32 it_value; diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index ce8bd7ac2a73..8c5f5a64a3a9 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -180,6 +180,44 @@ freebsd32_wait4(struct thread *td, struct freebsd32_wait4_args *uap) return (error); } +int +freebsd32_wait6(struct thread *td, struct freebsd32_wait6_args *uap) +{ + struct wrusage32 wru32; + struct __wrusage wru, *wrup; + struct siginfo32 si32; + struct __siginfo si, *sip; + int error, status; + + if (uap->wrusage != NULL) + wrup = &wru; + else + wrup = NULL; + + if (uap->info != NULL) { + sip = &si; + bzero(sip, sizeof(*sip)); + } else + sip = NULL; + + error = kern_wait6(td, uap->idtype, uap->id, &status, uap->options, + wrup, sip); + if (error != 0) + return (error); + if (uap->status != NULL) + error = copyout(&status, uap->status, sizeof(status)); + if (uap->wrusage != NULL && error == 0) { + freebsd32_rusage_out(&wru.wru_self, &wru32.wru_self); + freebsd32_rusage_out(&wru.wru_children, &wru32.wru_children); + error = copyout(&wru32, uap->wrusage, sizeof(wru32)); + } + if (uap->info != NULL && error == 0) { + siginfo_to_siginfo32 (&si, &si32); + error = copyout(&si32, uap->info, sizeof(si32)); + } + return (error); +} + #ifdef COMPAT_FREEBSD4 static void copy_statfs(struct statfs *in, struct statfs32 *out) diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master index 478558af1b48..41064475b99c 100644 --- a/sys/compat/freebsd32/syscalls.master +++ b/sys/compat/freebsd32/syscalls.master @@ -1000,3 +1000,8 @@ uint32_t offset1, uint32_t offset2,\ uint32_t len1, uint32_t len2, \ int advice); } +532 AUE_WAIT6 STD { int freebsd32_wait6(int idtype, int id, \ + int *status, int options, \ + struct wrusage32 *wrusage, \ + siginfo_t *info); } + diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index c04b992d848a..6b92c2501f38 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -143,7 +143,7 @@ exit1(struct thread *td, int rv) * XXX in case we're rebooting we just let init die in order to * work around an unsolved stack overflow seen very late during * shutdown on sparc64 when the gmirror worker process exists. - */ + */ if (p == initproc && rebooting == 0) { printf("init died (signal %d, exit %d)\n", WTERMSIG(rv), WEXITSTATUS(rv)); @@ -617,7 +617,7 @@ sys_abort2(struct thread *td, struct abort2_args *uap) sbuf_clear(sb); sbuf_printf(sb, "%s(pid %d uid %d) aborted: ", p->p_comm, p->p_pid, td->td_ucred->cr_uid); - /* + /* * Since we can't return from abort2(), send SIGKILL in cases, where * abort2() was called improperly */ @@ -689,7 +689,7 @@ owait(struct thread *td, struct owait_args *uap __unused) * The dirty work is handled by kern_wait(). */ int -sys_wait4(struct thread *td, struct wait_args *uap) +sys_wait4(struct thread *td, struct wait4_args *uap) { struct rusage ru, *rup; int error, status; @@ -706,14 +706,51 @@ sys_wait4(struct thread *td, struct wait_args *uap) return (error); } +int +sys_wait6(struct thread *td, struct wait6_args *uap) +{ + struct __wrusage wru, *wrup; + siginfo_t si, *sip; + int error, status; + idtype_t idtype; + id_t id; + + idtype = uap->idtype; + id = uap->id; + + if (uap->wrusage != NULL) + wrup = &wru; + else + wrup = NULL; + + if (uap->info != NULL) { + sip = &si; + bzero(sip, sizeof(*sip)); + } else + sip = NULL; + + /* + * We expect all callers of wait6() to know about WEXITED and + * WTRAPPED. + */ + error = kern_wait6(td, idtype, id, &status, uap->options, wrup, sip); + + if (uap->status != NULL && error == 0) + error = copyout(&status, uap->status, sizeof(status)); + if (uap->wrusage != NULL && error == 0) + error = copyout(&wru, uap->wrusage, sizeof(wru)); + if (uap->info != NULL && error == 0) + error = copyout(&si, uap->info, sizeof(si)); + return (error); +} + /* * Reap the remains of a zombie process and optionally return status and * rusage. Asserts and will release both the proctree_lock and the process * lock as part of its work. */ void -proc_reap(struct thread *td, struct proc *p, int *status, int options, - struct rusage *rusage) +proc_reap(struct thread *td, struct proc *p, int *status, int options) { struct proc *q, *t; @@ -723,10 +760,7 @@ proc_reap(struct thread *td, struct proc *p, int *status, int options, KASSERT(p->p_state == PRS_ZOMBIE, ("proc_reap: !PRS_ZOMBIE")); q = td->td_proc; - if (rusage) { - *rusage = p->p_ru; - calcru(p, &rusage->ru_utime, &rusage->ru_stime); - } + PROC_SUNLOCK(p); td->td_retval[0] = p->p_pid; if (status) @@ -839,20 +873,74 @@ proc_reap(struct thread *td, struct proc *p, int *status, int options, } static int -proc_to_reap(struct thread *td, struct proc *p, pid_t pid, int *status, - int options, struct rusage *rusage) +proc_to_reap(struct thread *td, struct proc *p, idtype_t idtype, id_t id, + int *status, int options, struct __wrusage *wrusage, siginfo_t *siginfo) { struct proc *q; + struct rusage *rup; sx_assert(&proctree_lock, SA_XLOCKED); q = td->td_proc; PROC_LOCK(p); - if (pid != WAIT_ANY && p->p_pid != pid && p->p_pgid != -pid) { + + switch (idtype) { + case P_ALL: + break; + case P_PID: + if (p->p_pid != (pid_t)id) { + PROC_UNLOCK(p); + return (0); + } + break; + case P_PGID: + if (p->p_pgid != (pid_t)id) { + PROC_UNLOCK(p); + return (0); + } + break; + case P_SID: + if (p->p_session->s_sid != (pid_t)id) { + PROC_UNLOCK(p); + return (0); + } + break; + case P_UID: + if (p->p_ucred->cr_uid != (uid_t)id) { + PROC_UNLOCK(p); + return (0); + } + break; + case P_GID: + if (p->p_ucred->cr_gid != (gid_t)id) { + PROC_UNLOCK(p); + return (0); + } + break; + case P_JAILID: + if (p->p_ucred->cr_prison == NULL || + (p->p_ucred->cr_prison->pr_id != (int)id)) { + PROC_UNLOCK(p); + return (0); + } + break; + /* + * It seems that the thread structures get zeroed out + * at process exit. This makes it impossible to + * support P_SETID, P_CID or P_CPUID. + */ + default: + PROC_UNLOCK(p); + return (0); + break; + } + + if (p_canwait(td, p)) { PROC_UNLOCK(p); return (0); } - if (p_canwait(td, p)) { + + if (((options & WEXITED) == 0) && (p->p_state == PRS_ZOMBIE)) { PROC_UNLOCK(p); return (0); } @@ -872,8 +960,59 @@ proc_to_reap(struct thread *td, struct proc *p, pid_t pid, int *status, } PROC_SLOCK(p); + + if (siginfo != NULL) { + bzero (siginfo, sizeof (*siginfo)); + siginfo->si_errno = 0; + + /* + * SUSv4 requires that the si_signo value is always + * SIGCHLD. Obey it despite the rfork(2) interface + * allows to request other signal for child exit + * notification. + */ + siginfo->si_signo = SIGCHLD; + + /* + * This is still a rough estimate. We will fix the + * cases TRAPPED, STOPPED, and CONTINUED later. + */ + if (WCOREDUMP(p->p_xstat)) + siginfo->si_code = CLD_DUMPED; + else if (WIFSIGNALED(p->p_xstat)) + siginfo->si_code = CLD_KILLED; + else + siginfo->si_code = CLD_EXITED; + + siginfo->si_pid = p->p_pid; + siginfo->si_uid = p->p_ucred->cr_uid; + siginfo->si_status = p->p_xstat; + + /* + * The si_addr field would be useful additional + * detail, but apparently the PC value may be lost + * when we reach this point. bzero() above sets + * siginfo->si_addr to NULL. + */ + } + + /* + * There should be no reason to limit resources usage info to + * exited processes only. A snapshot about any resources used + * by a stopped process may be exactly what is needed. + */ + if (wrusage != NULL) { + rup = &wrusage->wru_self; + *rup = p->p_ru; + calcru(p, &rup->ru_utime, &rup->ru_stime); + + rup = &wrusage->wru_children; + *rup = p->p_stats->p_cru; + calccru(p, &rup->ru_utime, &rup->ru_stime); + } + if (p->p_state == PRS_ZOMBIE) { - proc_reap(td, p, status, options, rusage); + proc_reap(td, p, status, options); return (-1); } PROC_SUNLOCK(p); @@ -884,22 +1023,72 @@ proc_to_reap(struct thread *td, struct proc *p, pid_t pid, int *status, int kern_wait(struct thread *td, pid_t pid, int *status, int options, struct rusage *rusage) +{ + struct __wrusage wru, *wrup; + idtype_t idtype; + id_t id; + int ret; + + if (pid == WAIT_ANY) { + idtype = P_ALL; + id = 0; + } + else if (pid <= 0) { + idtype = P_PGID; + id = (id_t)-pid; + } + else { + idtype = P_PID; + id = (id_t)pid; + } + if (rusage != NULL) + wrup = &wru; + else + wrup = NULL; + /* + * For backward compatibility we implicitly add flags WEXITED + * and WTRAPPED here. + */ + options |= WEXITED | WTRAPPED; + ret = kern_wait6(td, idtype, id, status, options, wrup, NULL); + if (rusage != NULL) + *rusage = wru.wru_self; + return (ret); +} + +int +kern_wait6(struct thread *td, idtype_t idtype, id_t id, int *status, + int options, struct __wrusage *wrusage, siginfo_t *siginfo) { struct proc *p, *q; int error, nfound, ret; - AUDIT_ARG_PID(pid); + AUDIT_ARG_VALUE((int)idtype); /* XXX - This is likely wrong! */ + AUDIT_ARG_PID((pid_t)id); /* XXX - This may be wrong! */ AUDIT_ARG_VALUE(options); q = td->td_proc; - if (pid == 0) { - PROC_LOCK(q); - pid = -q->p_pgid; - PROC_UNLOCK(q); + + if ((pid_t)id == WAIT_MYPGRP && + (idtype == P_PID || idtype == P_PGID)) { + id = (id_t)q->p_pgid; + idtype = P_PGID; } + /* If we don't know the option, just return. */ - if (options & ~(WUNTRACED|WNOHANG|WCONTINUED|WNOWAIT|WLINUXCLONE)) + if ((options & ~(WUNTRACED | WNOHANG | WCONTINUED | WNOWAIT | + WEXITED | WTRAPPED | WLINUXCLONE)) != 0) return (EINVAL); + if ((options & (WEXITED | WUNTRACED | WCONTINUED | WTRAPPED)) == 0) { + /* + * We will be unable to find any matching processes, + * because there are no known events to look for. + * Prefer to return error instead of blocking + * indefinitely. + */ + return (EINVAL); + } + loop: if (q->p_flag & P_STATCHILD) { PROC_LOCK(q); @@ -909,7 +1098,8 @@ kern_wait(struct thread *td, pid_t pid, int *status, int options, nfound = 0; sx_xlock(&proctree_lock); LIST_FOREACH(p, &q->p_children, p_sibling) { - ret = proc_to_reap(td, p, pid, status, options, rusage); + ret = proc_to_reap(td, p, idtype, id, status, options, + wrusage, siginfo); if (ret == 0) continue; else if (ret == 1) @@ -919,37 +1109,77 @@ kern_wait(struct thread *td, pid_t pid, int *status, int options, PROC_LOCK(p); PROC_SLOCK(p); - if ((p->p_flag & P_STOPPED_SIG) && + + if ((options & WTRAPPED) != 0 && + (p->p_flag & P_TRACED) != 0 && + (p->p_flag & (P_STOPPED_TRACE | P_STOPPED_SIG)) != 0 && (p->p_suspcount == p->p_numthreads) && - (p->p_flag & P_WAITED) == 0 && - (p->p_flag & P_TRACED || options & WUNTRACED)) { + ((p->p_flag & P_WAITED) == 0)) { PROC_SUNLOCK(p); - p->p_flag |= P_WAITED; + if ((options & WNOWAIT) == 0) + p->p_flag |= P_WAITED; sx_xunlock(&proctree_lock); td->td_retval[0] = p->p_pid; - if (status) + + if (status != NULL) *status = W_STOPCODE(p->p_xstat); + if (siginfo != NULL) { + siginfo->si_status = p->p_xstat; + siginfo->si_code = CLD_TRAPPED; + } + if ((options & WNOWAIT) == 0) { + PROC_LOCK(q); + sigqueue_take(p->p_ksi); + PROC_UNLOCK(q); + } - PROC_LOCK(q); - sigqueue_take(p->p_ksi); - PROC_UNLOCK(q); PROC_UNLOCK(p); + return (0); + } + if ((options & WUNTRACED) != 0 && + (p->p_flag & P_STOPPED_SIG) != 0 && + (p->p_suspcount == p->p_numthreads) && + ((p->p_flag & P_WAITED) == 0)) { + PROC_SUNLOCK(p); + if ((options & WNOWAIT) == 0) + p->p_flag |= P_WAITED; + sx_xunlock(&proctree_lock); + td->td_retval[0] = p->p_pid; + if (status != NULL) + *status = W_STOPCODE(p->p_xstat); + if (siginfo != NULL) { + siginfo->si_status = p->p_xstat; + siginfo->si_code = CLD_STOPPED; + } + if ((options & WNOWAIT) == 0) { + PROC_LOCK(q); + sigqueue_take(p->p_ksi); + PROC_UNLOCK(q); + } + + PROC_UNLOCK(p); return (0); } PROC_SUNLOCK(p); - if (options & WCONTINUED && (p->p_flag & P_CONTINUED)) { + if ((options & WCONTINUED) != 0 && + (p->p_flag & P_CONTINUED) != 0) { sx_xunlock(&proctree_lock); td->td_retval[0] = p->p_pid; - p->p_flag &= ~P_CONTINUED; - - PROC_LOCK(q); - sigqueue_take(p->p_ksi); - PROC_UNLOCK(q); + if ((options & WNOWAIT) == 0) { + p->p_flag &= ~P_CONTINUED; + PROC_LOCK(q); + sigqueue_take(p->p_ksi); + PROC_UNLOCK(q); + } PROC_UNLOCK(p); - if (status) + if (status != NULL) *status = SIGCONT; + if (siginfo != NULL) { + siginfo->si_status = SIGCONT; + siginfo->si_code = CLD_CONTINUED; + } return (0); } PROC_UNLOCK(p); @@ -968,7 +1198,8 @@ kern_wait(struct thread *td, pid_t pid, int *status, int options, * to successfully wait until the child becomes a zombie. */ LIST_FOREACH(p, &q->p_orphans, p_orphan) { - ret = proc_to_reap(td, p, pid, status, options, rusage); + ret = proc_to_reap(td, p, idtype, id, status, options, + wrusage, siginfo); if (ret == 0) continue; else if (ret == 1) @@ -994,7 +1225,7 @@ kern_wait(struct thread *td, pid_t pid, int *status, int options, error = msleep(q, &q->p_mtx, PWAIT | PCATCH, "wait", 0); PROC_UNLOCK(q); if (error) - return (error); + return (error); goto loop; } diff --git a/sys/kern/sys_procdesc.c b/sys/kern/sys_procdesc.c index ac625f7f780b..f40004182f53 100644 --- a/sys/kern/sys_procdesc.c +++ b/sys/kern/sys_procdesc.c @@ -374,7 +374,7 @@ procdesc_close(struct file *fp, struct thread *td) */ PROC_LOCK(p); PROC_SLOCK(p); - proc_reap(curthread, p, NULL, 0, NULL); + proc_reap(curthread, p, NULL, 0); } else { /* * If the process is not yet dead, we need to kill it, but we diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 5cb90f8aa372..148dea3e4992 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -71,8 +71,7 @@ ; XXX man page says `mode_t mode'. 6 AUE_CLOSE STD { int close(int fd); } 7 AUE_WAIT4 STD { int wait4(int pid, int *status, \ - int options, struct rusage *rusage); } \ - wait4 wait_args int + int options, struct rusage *rusage); } 8 AUE_CREAT COMPAT { int creat(char *path, int mode); } 9 AUE_LINK STD { int link(char *path, char *link); } 10 AUE_UNLINK STD { int unlink(char *path); } @@ -952,5 +951,9 @@ off_t offset, off_t len); } 531 AUE_NULL STD { int posix_fadvise(int fd, off_t offset, \ off_t len, int advice); } +532 AUE_WAIT6 STD { int wait6(int idtype, int id, \ + int *status, int options, \ + struct __wrusage *wrusage, \ + siginfo_t *info); } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 567cb63bf410..ff920501b438 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -885,8 +885,7 @@ int proc_getenvv(struct thread *td, struct proc *p, struct sbuf *sb); void procinit(void); void proc_linkup0(struct proc *p, struct thread *td); void proc_linkup(struct proc *p, struct thread *td); -void proc_reap(struct thread *td, struct proc *p, int *status, int options, - struct rusage *rusage); +void proc_reap(struct thread *td, struct proc *p, int *status, int options); void proc_reparent(struct proc *child, struct proc *newparent); struct pstats *pstats_alloc(void); void pstats_fork(struct pstats *src, struct pstats *dst); diff --git a/sys/sys/resource.h b/sys/sys/resource.h index c5e912bdaa6c..89dc967c2334 100644 --- a/sys/sys/resource.h +++ b/sys/sys/resource.h @@ -79,6 +79,13 @@ struct rusage { #define ru_last ru_nivcsw }; +#if __BSD_VISIBLE +struct __wrusage { + struct rusage wru_self; + struct rusage wru_children; +}; +#endif + /* * Resource limits */ diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index 4335550c3192..48216a517068 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -43,6 +43,7 @@ struct msghdr; struct msqid_ds; struct rlimit; struct rusage; +struct __wrusage; union semun; struct sockaddr; struct stat; @@ -234,6 +235,8 @@ int kern_utimesat(struct thread *td, int fd, char *path, enum uio_seg pathseg, struct timeval *tptr, enum uio_seg tptrseg); int kern_wait(struct thread *td, pid_t pid, int *status, int options, struct rusage *rup); +int kern_wait6(struct thread *td, idtype_t idtype, id_t id, int *status, + int options, struct __wrusage *wrup, siginfo_t *sip); int kern_writev(struct thread *td, int fd, struct uio *auio); int kern_socketpair(struct thread *td, int domain, int type, int protocol, int *rsv); diff --git a/sys/sys/types.h b/sys/sys/types.h index 491e99daefc4..bef30da93bad 100644 --- a/sys/sys/types.h +++ b/sys/sys/types.h @@ -141,6 +141,46 @@ typedef __id_t id_t; /* can hold a uid_t or pid_t */ #define _ID_T_DECLARED #endif +#ifndef _IDTYPE_T_DECLARED + +typedef enum +#if defined(__BSD_VISIBLE) + idtype /* pollutes XPG4.2 namespace */ +#endif + { + /* + * These names were mostly lifted from Solaris source code and + * still use Solaris style naming to avoid breaking any + * OpenSolaris code which has been ported to FreeBSD. There + * is no clear FreeBSD counterpart for all of the names, but + * some have a clear correspondence to FreeBSD entities. + */ + P_PID, /* A process identifier. */ + P_PPID, /* A parent process identifier. */ + P_PGID, /* A process group identifier. */ + P_SID, /* A session identifier. */ + P_CID, /* A scheduling class identifier. */ + P_UID, /* A user identifier. */ + P_GID, /* A group identifier. */ + P_ALL, /* All processes. */ + P_LWPID, /* An LWP identifier. */ + P_TASKID, /* A task identifier. */ + P_PROJID, /* A project identifier. */ + P_POOLID, /* A pool identifier. */ + P_JAILID, /* A zone identifier. */ + P_CTID, /* A (process) contract identifier. */ + P_CPUID, /* CPU identifier. */ + P_PSETID /* Processor set identifier */ +} idtype_t; /* The type of id_t we are using. */ + +#if defined(__BSD_VISIBLE) +#define P_ZONEID P_JAILID +#endif + +#define _IDTYPE_T_DECLARED +#endif + + #ifndef _INO_T_DECLARED typedef __ino_t ino_t; /* inode number */ #define _INO_T_DECLARED diff --git a/sys/sys/wait.h b/sys/sys/wait.h index 0e23c23455ee..113429f996f1 100644 --- a/sys/sys/wait.h +++ b/sys/sys/wait.h @@ -80,6 +80,9 @@ #define WSTOPPED WUNTRACED /* SUS compatibility */ #define WCONTINUED 4 /* Report a job control continued process. */ #define WNOWAIT 8 /* Poll only. Don't delete the proc entry. */ +#define WEXITED 16 /* Wait for exited processes. */ +#define WTRAPPED 32 /* Wait for a process to hit a trap or + a breakpoint. */ #if __BSD_VISIBLE #define WLINUXCLONE 0x80000000 /* Wait for kthread spawned from linux_clone. */ @@ -87,6 +90,8 @@ /* * Tokens for special values of the "pid" parameter to wait4. + * Extended struct __wrusage to collect rusage for both the target + * process and its children within one wait6() call. */ #if __BSD_VISIBLE #define WAIT_ANY (-1) /* any process */ @@ -97,12 +102,19 @@ #include __BEGIN_DECLS +struct __siginfo; pid_t wait(int *); pid_t waitpid(pid_t, int *, int); +#if __POSIX_VISIBLE >= 200112 +int waitid(idtype_t, id_t, struct __siginfo *, int); +#endif #if __BSD_VISIBLE struct rusage; +struct __wrusage; pid_t wait3(int *, int, struct rusage *); pid_t wait4(pid_t, int *, int, struct rusage *); +pid_t wait6(idtype_t, id_t, int *, int, struct __wrusage *, + struct __siginfo *); #endif __END_DECLS #endif /* !_KERNEL */