diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index 2481f28..906f4ce 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -34,7 +34,7 @@ SRCS+= __getosreldate.c __xuname.c \ syslog.c telldir.c termios.c time.c times.c timezone.c tls.c \ ttyname.c ttyslot.c ualarm.c ulimit.c uname.c unvis.c \ usleep.c utime.c utxdb.c valloc.c vis.c wait.c wait3.c waitpid.c \ - wordexp.c + waitid.c wordexp.c .PATH: ${.CURDIR}/../../contrib/libc-pwcache SRCS+= pwcache.c pwcache.h diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map index b4f88ae..f5d4f71 100644 --- a/lib/libc/gen/Symbol.map +++ b/lib/libc/gen/Symbol.map @@ -391,6 +391,7 @@ FBSD_1.3 { pwcache_userdb; pwcache_groupdb; uid_from_user; + waitid; }; FBSDprivate_1.0 { diff --git a/lib/libc/gen/waitid.c b/lib/libc/gen/waitid.c new file mode 100644 index 0000000..34c949d --- /dev/null +++ b/lib/libc/gen/waitid.c @@ -0,0 +1,41 @@ +/*- + * XXX + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include +#include +#include +#include +#include +#include +#include "un-namespace.h" + +int +__waitid(idtype_t idtype, id_t id, siginfo_t *info, int flags) +{ + int status; + pid_t ret; + + ret = _wait6(idtype, id, &status, flags, NULL, info); + + /* + * According to SUSv4, waitid() shall not return a PID when a + * process is found, but only 0. If a process was actually + * found, siginfo_t fields si_signo and si_pid will be + * non-zero. In case WNOHANG was set in the flags and no + * process was found those fields are set to zero using + * memset() below. + */ + if (ret == 0 && info != NULL) + memset(info, 0, sizeof(*info)); + else if (ret > 0) + ret = 0; + return (ret); +} + +__weak_reference(__waitid, waitid); +__weak_reference(__waitid, _waitid); diff --git a/lib/libc/include/namespace.h b/lib/libc/include/namespace.h index 1e00030..739d7b1 100644 --- a/lib/libc/include/namespace.h +++ b/lib/libc/include/namespace.h @@ -229,6 +229,7 @@ #define socketpair _socketpair #define usleep _usleep #define wait4 _wait4 +#define wait6 _wait6 #define waitpid _waitpid #define write _write #define writev _writev diff --git a/lib/libc/include/un-namespace.h b/lib/libc/include/un-namespace.h index 9c9ce97..f31fa7a 100644 --- a/lib/libc/include/un-namespace.h +++ b/lib/libc/include/un-namespace.h @@ -210,6 +210,7 @@ #undef socketpair #undef usleep #undef wait4 +#undef wait6 #undef waitpid #undef write #undef writev diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index df4ef42..a35ed5d 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -217,5 +217,6 @@ MLINKS+=timer_settime.2 timer_getoverrun.2 timer_settime.2 timer_gettime.2 MLINKS+=truncate.2 ftruncate.2 MLINKS+=unlink.2 unlinkat.2 MLINKS+=utimes.2 futimes.2 utimes.2 futimesat.2 utimes.2 lutimes.2 -MLINKS+=wait.2 wait3.2 wait.2 wait4.2 wait.2 waitpid.2 +MLINKS+=wait.2 wait3.2 wait.2 wait4.2 wait.2 waitpid.2 \ + wait.2 waitid.2 wait.2 wait6.2 MLINKS+=write.2 pwrite.2 write.2 pwritev.2 write.2 writev.2 diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index 008b8da..babae30 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -384,6 +384,7 @@ FBSD_1.3 { ffclock_getestimate; ffclock_setestimate; posix_fadvise; + wait6; }; FBSDprivate_1.0 { @@ -1019,6 +1020,8 @@ FBSDprivate_1.0 { __sys_vadvise; _wait4; __sys_wait4; + _wait6; + __sys_wait6; _write; __sys_write; _writev; diff --git a/lib/libc/sys/wait.2 b/lib/libc/sys/wait.2 index 71bd529..63aaad3 100644 --- a/lib/libc/sys/wait.2 +++ b/lib/libc/sys/wait.2 @@ -34,9 +34,11 @@ .Sh NAME .Nm wait , .Nm waitpid , +.Nm waitid , +.Nm wait3 , .Nm wait4 , -.Nm wait3 -.Nd wait for process termination +.Nm wait6 +.Nd wait for processes to change status .Sh LIBRARY .Lb libc .Sh SYNOPSIS @@ -46,12 +48,17 @@ .Fn wait "int *status" .Ft pid_t .Fn waitpid "pid_t wpid" "int *status" "int options" +.In sys/signal.h +.Ft int +.Fn waitid "idtype_t idtype" "id_t id" "siginfo_t *info" "int options" .In sys/time.h .In sys/resource.h .Ft pid_t .Fn wait3 "int *status" "int options" "struct rusage *rusage" .Ft pid_t .Fn wait4 "pid_t wpid" "int *status" "int options" "struct rusage *rusage" +.Ft pid_t +.Fn wait6 "idtype_t idtype" "id_t id" "int *status" "int options" "struct wrusage *wrusage" "siginfo_t *infop" .Sh DESCRIPTION The .Fn wait @@ -86,28 +93,181 @@ system call provides a more general interface for programs that need to wait for certain child processes, that need resource utilization statistics accumulated by child processes, or that require options. -The other wait functions are implemented using -.Fn wait4 . .Pp +The broadest interface of all functions in this family is +.Fn wait6 +which is otherwise very much like +.Fn wait4 +but with a few very important distinctions. +To wait for exited processes, the option flag +.Dv WEXITED +need to be explicitly specified. +This allows for waiting for processes which have experienced other +status changes without having to handle also the exit status from +the terminated processes. +Instead of the traditional +.Dv rusage +argument, a pointer to a new structure +.Bd -literal +struct wrusage { + struct rusage wru_self; + struct rusage wru_children; +}; +.Ed +can be passed. +This allows the calling process to collect resource usage statistics +from both its own child process as well as from its grand children. +When no resource usage statistics are needed this pointer can be +.Dv NULL . +The last argument +.Fa infop +must be either +.Dv NULL +or a pointer to a +.Fa siginfo_t +structure. +When specified, the structure is filled the same as for +.Dv SIGNCHLD +signal, delivered at the process state change. +.br +The process, which state is queried, is specified by two arguments +.Fa idtype_t +and +.Fa id_t . +The separate +.Fa idtype +and +.Fa id +arguments allows to support many other types of +IDs as well in addition to PID and PGID. +.Pp +The +.Fa idtype +and +.Fa id +arguments specify which processes +.Fn waitid +and +.Fn wait6 +shall wait for. +.Bl -bullet -offset indent +.It +If +.Fa idtype +is +.Dv P_PID , +.Fn waitid +and +.Fn wait6 +wait for the child process with a process ID equal to +.Dv (pid_t)id . +.It +If +.Fa idtype +is +.Dv P_PGID , +.Fn waitid +and +.Fn wait6 +wait for the child process with a process group ID equal to +.Dv (pid_t)id . +.It +If +.Fa idtype +is +.Dv P_ALL , +.Fn waitid +and +.Fn wait6 +wait for any child process and the +.Dv id +is ignored. +.It +If +.Fa idtype +is +.Dv P_PID +or +.Dv P_PGID +and the +.Dv id +is zero, +.Fn waitid +and +.Fn wait6 +wait for any child process in the same process group as the caller. +.El +Non-standard specifiers for the process to wait for, supported by this +implementation of +.Fn waitid +and +.Fn wait6 , +are: +.Bl -bullet -offset indent +.It +The +.Fa idtype +value +.Dv P_UID +waits for processes which effective UID is equal to +.Dv (uid_t)id . +.It +The +.Fa idtype +value +.Dv P_GID +waits for processes which effective GID is equal to +.Dv (gid_t)id . +.It The +.Fa idtype +value +.Dv P_SID +waits for processes which session ID is equal to +.Dv id . +In case the child process started its own new session, +SID will be the same as its own PID. +Otherwise the SID of a child process will match the caller's SID. +.It +The +.Fa idtype +value +.Dv P_JAILID +waits for processes within a jail which jail identifier is equal +to +.Dv id . +.El +.Pp +For +.Fn wait , +.Fn wait3 , +and +.Fn wait4 +functions, the .Fa wpid argument specifies the set of child processes for which to wait. +.Bl -bullet -offset indent +.It If .Fa wpid is -1, the call waits for any child process. +.It If .Fa wpid is 0, the call waits for any child process in the process group of the caller. +.It If .Fa wpid is greater than zero, the call waits for the process with process id .Fa wpid . +.It If .Fa wpid is less than -1, the call waits for any process whose process group id equals the absolute value of .Fa wpid . +.El .Pp The .Fa status @@ -116,41 +276,96 @@ argument is defined below. The .Fa options argument contains the bitwise OR of any of the following options. -The -.Dv WCONTINUED -option indicates that children of the current process that +.Bl -tag -width Ds +.It Dv WCONTINUED +indicates that children of the current process that have continued from a job control stop, by receiving a .Dv SIGCONT signal, should also have their status reported. -The -.Dv WNOHANG -option -is used to indicate that the call should not block if -there are no processes that wish to report status. -If the -.Dv WUNTRACED -option is set, -children of the current process that are stopped +.It Dv WNOHANG +is used to indicate that the call should not block when +there are no processes wishing to report status. +.It Dv WUNTRACED +indicates that children of the current process which are stopped due to a .Dv SIGTTIN , SIGTTOU , SIGTSTP , or .Dv SIGSTOP -signal also have their status reported. -The -.Dv WSTOPPED -option is an alias for +signal shall have their status reported. +.It Dv WSTOPPED +is an alias for .Dv WUNTRACED . -The -.Dv WNOWAIT -option keeps the process whose status is returned in a waitable state. +.It Dv WTRAPPED +allows waiting for processes which have trapped or reached a breakpoint. +.It Dv WEXITED +indicates that the caller is wants to receive status reports from +terminated processes. +This flag is implicitly set for the older functions +.Fn wait , +.Fn waitpid , +.Fn wait3 , +and +.Fn wait4 . +.br +For the +.Fn waitid +and +.Fn wait6 +functions, the flag has to be explicitly included in the +.Fa options , +if status reports from terminated processes are expected. +.It Dv WNOWAIT +keeps the process whose status is returned in a waitable state. The process may be waited for again after this call completes. +.El +.sp +For the +.Fn waitid +and +.Fn wait6 +functions, at least one of the options +.Dv WEXITED , +.Dv WUNTRACED , +.Dv WSTOPPED , +.Dv WTRAPPED , +or +.Dv WCONTINUED +must be specified. +Otherwise there will be no events for the call to report. +To avoid hanging indefinitely in such a case these functions +return -1 with +.Dv errno +set to +.Dv EINVAL . .Pp If .Fa rusage is non-zero, a summary of the resources used by the terminated -process and all its -children is returned (this information is currently not available -for stopped or continued processes). +process and all its children is returned. +.Pp +If +.Fa infop +is non-NULL, it must point to a +.Dv siginfo_t +structure which is filled on return such that the +.Dv si_signo +field is always +.Dv SIGCHLD +and the field +.Dv si_pid +if be non-zero, if there is a status change to report. +If there are no status changes to report and WNOHANG is applied, +both of these fields are returned zero. +When using the +.Fn waitid +function with the +.Dv WNOHANG +option set, checking these fields is the only way to know whether +there were any status changes to report, because the return value +from +.Fn waitid +is be zero as it is for any successful return from +.Fn waitid . .Pp When the .Dv WNOHANG @@ -175,6 +390,20 @@ call is the same as with a .Fa wpid value of -1. +The +.Fn wait6 +call, with the bits +.Dv WEXITED +and +.Dv WTRAPPED +set in the +.Fa options +and with +.Fa infop +set to +.Dv NULL , +is similar to +.Fn wait4 . .Pp The following macros may be used to test the manner of exit of the process. One of the first four macros will evaluate to a non-zero (true) value: @@ -284,6 +513,7 @@ is returned and is set to indicate the error. .Pp If +.Fn wait6 , .Fn wait4 , .Fn wait3 , or @@ -306,6 +536,18 @@ a value of -1 is returned and .Va errno is set to indicate the error. +.Pp +If +.Fn waitid +returns because one or more processes have a state change to report, +0 is returned. +To indicate an error, -1 will be returned and +.Dv errno +set to an appropriate value. +If +.Dv WNOHANG +was used, 0 can be returned indicating no error, but no processes +may have changed state either, if si_signo and/or si_pid are zero. .Sh ERRORS The .Fn wait @@ -335,6 +577,14 @@ The call was interrupted by a caught signal, or the signal did not have the .Dv SA_RESTART flag set. +.It Bq Er EINVAL +An invalid value was specified for +.Fa options , +or +.Fa idtype +and +.Fa id +do not specify a valid set of processes. .El .Sh SEE ALSO .Xr _exit 2 , @@ -344,11 +594,13 @@ flag set. .Xr siginfo 3 .Sh STANDARDS The -.Fn wait +.Fn wait , +.Fn waitpid , and -.Fn waitpid +.Fn waitid functions are defined by POSIX; -.Fn wait4 +.Fn wait6 , +.Fn wait4 , and .Fn wait3 are not specified by POSIX. diff --git a/sys/bsm/audit_kevents.h b/sys/bsm/audit_kevents.h index f4f77a5..3eb2e3a 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 c367c93..8c5739b 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 fccdef0..a95b0e5 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 ce8bd7a..6d1357b 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 478558a..4106447 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 c04b992..594793e 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,24 +873,78 @@ 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 (((options & WEXITED) == 0) && (p->p_state == PRS_ZOMBIE)) { + PROC_UNLOCK(p); + return (0); + } + /* * This special case handles a kthread spawned by linux_clone * (see linux_misc.c). The linux_wait4 and linux_waitpid @@ -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); @@ -885,21 +1024,73 @@ 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) + 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 +1100,8 @@ loop: 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 +1111,77 @@ loop: 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 +1200,8 @@ loop: * 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 +1227,7 @@ loop: 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/syscalls.master b/sys/kern/syscalls.master index 5cb90f8..5c04ee6 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -72,7 +72,7 @@ 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 + wait4 wait4_args int 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 +952,10 @@ 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); } \ + wait6 wait6_args int ; 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 567cb63..ff92050 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 c5e912b..469f013 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 4335550..96a679e 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 491e99d..bef30da 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 0e23c23..b0e0c08 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,18 @@ #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 */