]> git.hungrycats.org Git - linux/commitdiff
[PATCH] waitid system call
authorRoland McGrath <roland@redhat.com>
Tue, 31 Aug 2004 03:35:25 +0000 (20:35 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Tue, 31 Aug 2004 03:35:25 +0000 (20:35 -0700)
This patch adds a new system call `waitid'.  This is a new POSIX call that
subsumes the rest of the wait* family and can do some things the older
calls cannot.  A minor addition is the ability to select what kinds of
status to check for with a mask of independent bits, so you can wait for
just stops and not terminations, for example.  A more significant
improvement is the WNOWAIT flag, which allows for polling child status
without reaping.  This interface fills in a siginfo_t with the same details
that a SIGCHLD for the status change has; some of that info (e.g.  si_uid)
is not available via wait4 or other calls.

I've added a new system call that has the parameter conventions of the
POSIX function because that seems like the cleanest thing.  This patch
includes the actual system call table additions for i386 and x86-64; other
architectures will need to assign the system call number, and 64-bit ones
may need to implement 32-bit compat support for it as I did for x86-64.
The new features could instead be provided by some new kludge inventions in
the wait4 system call interface (that's what BSD did).  If kludges are
preferable to adding a system call, I can work up something different.

I added a struct rusage field si_rusage to siginfo_t in the SIGCHLD case
(this does not affect the size or layout of the struct).  This is not part
of the POSIX interface, but it makes it so that `waitid' subsumes all the
functionality of `wait4'.  Future kernel ABIs (new arch's or whatnot) can
have only the `waitid' system call and the rest of the wait* family
including wait3 and wait4 can be implemented in user space using waitid.
There is nothing in user space as yet that would make use of the new field.

Most of the new functionality is implemented purely in the waitid system
call itself.  POSIX also provides for the WCONTINUED flag to report when a
child process had been stopped by job control and then resumed with
SIGCONT.  Corresponding to this, a SIGCHLD is now generated when a child
resumes (unless SA_NOCLDSTOP is set), with the value CLD_CONTINUED in
siginfo_t.si_code.  To implement this, some additional bookkeeping is
required in the signal code handling job control stops.

The motivation for this work is to make it possible to implement the POSIX
semantics of the `waitid' function in glibc completely and correctly.  If
changing either the system call interface used to accomplish that, or any
details of the kernel implementation work, would improve the chances of
getting this incorporated, I am more than happy to work through any issues.

Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
18 files changed:
arch/i386/kernel/entry.S
arch/x86_64/ia32/ia32_signal.c
arch/x86_64/ia32/ia32entry.S
arch/x86_64/ia32/sys_ia32.c
include/asm-generic/siginfo.h
include/asm-i386/unistd.h
include/asm-ia64/siginfo.h
include/asm-x86_64/ia32.h
include/asm-x86_64/ia32_unistd.h
include/asm-x86_64/unistd.h
include/linux/compat.h
include/linux/sched.h
include/linux/syscalls.h
include/linux/wait.h
kernel/compat.c
kernel/exit.c
kernel/signal.c
kernel/sys.c

index a3a571771f1492aa42a779cf9e5a347c9930558e..0504da0ce4325d991b17dbbc2686740b17a54af5 100644 (file)
@@ -900,5 +900,6 @@ ENTRY(sys_call_table)
        .long sys_mq_notify
        .long sys_mq_getsetattr
        .long sys_ni_syscall            /* reserved for kexec */
+       .long sys_waitid
 
 syscall_table_size=(.-sys_call_table)
index b5b77dbf3b400879c4e1d8bd33a7d1af5402f4d9..99b0f71e70c06892aeb7a993995ced02a5248ee5 100644 (file)
@@ -74,6 +74,8 @@ int ia32_copy_siginfo_to_user(siginfo_t32 __user *to, siginfo_t *from)
                        err |= __put_user(from->si_utime, &to->si_utime);
                        err |= __put_user(from->si_stime, &to->si_stime);
                        err |= __put_user(from->si_status, &to->si_status);
+                       err |= put_compat_rusage(&from->si_rusage,
+                                                &to->si_rusage);
                default:
                case __SI_KILL >> 16:
                        err |= __put_user(from->si_uid, &to->si_uid);
index 9fd8d5b3471dec67640e4506e5003dd15fb89fbb..7acd7963b9db339f81ecc53fc3f84c05352de373 100644 (file)
@@ -586,6 +586,7 @@ ia32_sys_call_table:
        .quad compat_sys_mq_notify
        .quad compat_sys_mq_getsetattr
        .quad quiet_ni_syscall          /* reserved for kexec */
+       .quad sys32_waitid
        /* don't forget to change IA32_NR_syscalls */
 ia32_syscall_end:              
        .rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8
index 6a33dbb93b71d0dcebda7560764cfc9175131cf7..96b0c653e216556aa49b1e052e7a7fb8db8aa6a9 100644 (file)
@@ -1151,6 +1151,25 @@ asmlinkage long sys32_clone(unsigned int clone_flags, unsigned int newsp,
         return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid);
 }
 
+asmlinkage long sys32_waitid(int which, compat_pid_t pid,
+                            siginfo_t32 __user *uinfo, int options)
+{
+       siginfo_t info;
+       long ret;
+       mm_segment_t old_fs = get_fs();
+
+       info.si_signo = 0;
+       set_fs (KERNEL_DS);
+       ret = sys_waitid(which, pid, (siginfo_t __user *) &info, options);
+       set_fs (old_fs);
+
+       if (ret < 0 || info.si_signo == 0)
+               return ret;
+       BUG_ON(info.si_code & __SI_MASK);
+       info.si_code |= __SI_CHLD;
+       return ia32_copy_siginfo_to_user(uinfo, &info);
+}
+
 /*
  * Some system calls that need sign extended arguments. This could be done by a generic wrapper.
  */ 
index f757d2d51e7f3deea6c98efdf2fa7f6c3c8e8abc..eaacc06baec30cee2c499965bd7ad21fe14d188b 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <linux/compiler.h>
 #include <linux/types.h>
+#include <linux/resource.h>
 
 typedef union sigval {
        int sival_int;
@@ -74,6 +75,7 @@ typedef struct siginfo {
                        int _status;            /* exit code */
                        clock_t _utime;
                        clock_t _stime;
+                       struct rusage _rusage;
                } _sigchld;
 
                /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
@@ -105,6 +107,7 @@ typedef struct siginfo {
 #define si_status      _sifields._sigchld._status
 #define si_utime       _sifields._sigchld._utime
 #define si_stime       _sifields._sigchld._stime
+#define si_rusage      _sifields._sigchld._rusage
 #define si_value       _sifields._rt._sigval
 #define si_int         _sifields._rt._sigval.sival_int
 #define si_ptr         _sifields._rt._sigval.sival_ptr
index ef936b8614045e030dc1594fd7d255ee1a49db8a..be8c6ac56c7c5f7a1207fb020fd9f756d3e278f5 100644 (file)
 #define __NR_mq_notify         (__NR_mq_open+4)
 #define __NR_mq_getsetattr     (__NR_mq_open+5)
 #define __NR_sys_kexec_load    283
+#define __NR_waitid            284
 
-#define NR_syscalls 284
+#define NR_syscalls 285
 
 /* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
 
index 52cce1d3dad86de4e885d20ddf31d4a3fcc566b2..cdaff790197f7bf962d3958aecfb1b5608dc6c7d 100644 (file)
@@ -56,6 +56,7 @@ typedef struct siginfo {
                        int _status;            /* exit code */
                        clock_t _utime;
                        clock_t _stime;
+                       struct rusage _rusage;
                } _sigchld;
 
                /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
index 73319a9a8ca5dd853e2a4d55e687873e0b0d10c1..e5fb979694280965ef2cf53c5945032db6ea7973 100644 (file)
@@ -115,6 +115,7 @@ typedef struct siginfo32 {
                        int _status;            /* exit code */
                        compat_clock_t _utime;
                        compat_clock_t _stime;
+                       struct compat_rusage _rusage;
                } _sigchld;
 
                /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
index ff0309c2ecf4b31362fd0c0d3c1cc9cae46f84e5..26e56c27fe186bd284f79ac6fb8ecd3af0786af2 100644 (file)
 #define __NR_ia32_mq_notify            (__NR_ia32_mq_open+4)
 #define __NR_ia32_mq_getsetattr        (__NR_ia32_mq_open+5)
 #define __NR_ia32_kexec                283
+#define __NR_ia32_waitid               284
 
-#define IA32_NR_syscalls 287   /* must be > than biggest syscall! */
+#define IA32_NR_syscalls 285   /* must be > than biggest syscall! */
 
 #endif /* _ASM_X86_64_IA32_UNISTD_H_ */
index 26e0aa30b5344be8141bd3b4c936f1f71ffe3c20..ae066360d59767c98711bf11ad7df37b9d70c9e4 100644 (file)
@@ -554,8 +554,10 @@ __SYSCALL(__NR_mq_notify, sys_mq_notify)
 __SYSCALL(__NR_mq_getsetattr, sys_mq_getsetattr)
 #define __NR_kexec_load        246
 __SYSCALL(__NR_kexec_load, sys_ni_syscall)
+#define __NR_waitid            (253)
+__SYSCALL(__NR_waitid, sys_waitid)
 
-#define __NR_syscall_max __NR_kexec_load
+#define __NR_syscall_max __NR_waitid
 #ifndef __NO_STUBS
 
 /* user-visible error numbers are in the range -1 - -4095 */
index e45c0f905451ac2f4696dac1010c7f30bcd833ba..5e58d705ac7c5721cf8ff9b0198528816ba55e0b 100644 (file)
@@ -79,6 +79,8 @@ struct compat_rusage {
        compat_long_t   ru_nivcsw;
 };
 
+extern int put_compat_rusage(const struct rusage *, struct compat_rusage __user *);
+
 struct compat_dirent {
        u32             d_ino;
        compat_off_t    d_off;
index 5a36441e2fd76ab2a61c33bb7139dc05f7a99dc1..f89a32a4bf4822bd5ec2b2f83aa39589acc24b3c 100644 (file)
@@ -287,6 +287,8 @@ struct signal_struct {
 
        /* thread group stop support, overloads group_exit_code too */
        int                     group_stop_count;
+       /* 1 if group stopped since last SIGCONT, -1 if SIGCONT since report */
+       int                     stop_state;
 
        /* POSIX.1b Interval Timers */
        struct list_head posix_timers;
index fadfc6d7a87e886ae959cdcda796554c7e17cae1..5ff82aafa59ea3de839555496aca6f9ad0f2f8df 100644 (file)
@@ -162,6 +162,8 @@ asmlinkage long sys_exit(int error_code);
 asmlinkage void sys_exit_group(int error_code);
 asmlinkage long sys_wait4(pid_t pid, unsigned int __user *stat_addr,
                                int options, struct rusage __user *ru);
+asmlinkage long sys_waitid(int which, pid_t pid,
+                               struct siginfo __user *infop, int options);
 asmlinkage long sys_waitpid(pid_t pid, unsigned int __user *stat_addr, int options);
 asmlinkage long sys_set_tid_address(int __user *tidptr);
 asmlinkage long sys_futex(u32 __user *uaddr, int op, int val,
index 98c8a441befaae3373be46580b975d4263eade92..4417f800a63952cf4b05d476676c5c4f2d0ad32f 100644 (file)
@@ -3,11 +3,20 @@
 
 #define WNOHANG                0x00000001
 #define WUNTRACED      0x00000002
+#define WSTOPPED       WUNTRACED
+#define WEXITED                0x00000004
+#define WCONTINUED     0x00000008
+#define WNOWAIT                0x01000000      /* Don't reap, just poll status.  */
 
 #define __WNOTHREAD    0x20000000      /* Don't wait on children of other threads in this group */
 #define __WALL         0x40000000      /* Wait on all children, regardless of type */
 #define __WCLONE       0x80000000      /* Wait only on non-SIGCHLD children */
 
+/* First argument to waitid: */
+#define P_ALL          0
+#define P_PID          1
+#define P_PGID         2
+
 #ifdef __KERNEL__
 
 #include <linux/config.h>
index 481ac0d4bb98482cd21d9613d0ed6754280c926e..5d0a92498deab4b4b7824e8c65f344dfab7af238 100644 (file)
@@ -310,7 +310,7 @@ asmlinkage long compat_sys_getrlimit (unsigned int resource,
        return ret;
 }
 
-static long put_compat_rusage(struct compat_rusage __user *ru, struct rusage *r)
+int put_compat_rusage(const struct rusage *r, struct compat_rusage __user *ru)
 {
        if (!access_ok(VERIFY_WRITE, ru, sizeof(*ru)) ||
            __put_user(r->ru_utime.tv_sec, &ru->ru_utime.tv_sec) ||
@@ -348,7 +348,7 @@ asmlinkage long compat_sys_getrusage(int who, struct compat_rusage __user *ru)
        if (ret)
                return ret;
 
-       if (put_compat_rusage(ru, &r))
+       if (put_compat_rusage(&r, ru))
                return -EFAULT;
 
        return 0;
@@ -374,7 +374,7 @@ compat_sys_wait4(compat_pid_t pid, compat_uint_t __user *stat_addr, int options,
                set_fs (old_fs);
 
                if (ret > 0) {
-                       if (put_compat_rusage(ru, &r)) 
+                       if (put_compat_rusage(&r, ru))
                                return -EFAULT;
                        if (stat_addr && put_user(status, stat_addr))
                                return -EFAULT;
index c6ceaaee8a2e3f6cc82aed8aecdf28a7cc98ef1d..a8c98b4880aaa0f3b4765bed4370118949526d01 100644 (file)
@@ -957,16 +957,64 @@ static int eligible_child(pid_t pid, int options, task_t *p)
        return 1;
 }
 
+static int wait_noreap_copyout(task_t *p, pid_t pid, uid_t uid,
+                              int why, int status,
+                              struct siginfo __user *infop)
+{
+       int retval = getrusage(p, RUSAGE_BOTH, &infop->si_rusage);
+       put_task_struct(p);
+       if (!retval)
+               retval = put_user(SIGCHLD, &infop->si_signo);
+       if (!retval)
+               retval = put_user(0, &infop->si_errno);
+       if (!retval)
+               retval = put_user((short)why, &infop->si_code);
+       if (!retval)
+               retval = put_user(pid, &infop->si_pid);
+       if (!retval)
+               retval = put_user(uid, &infop->si_uid);
+       if (!retval)
+               retval = put_user(status, &infop->si_status);
+       if (!retval)
+               retval = pid;
+       return retval;
+}
+
 /*
  * Handle sys_wait4 work for one task in state TASK_ZOMBIE.  We hold
  * read_lock(&tasklist_lock) on entry.  If we return zero, we still hold
  * the lock and this task is uninteresting.  If we return nonzero, we have
  * released the lock and the system call should return.
  */
-static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct rusage __user *ru)
+static int wait_task_zombie(task_t *p, int noreap,
+                           struct siginfo __user *infop,
+                           int __user *stat_addr, struct rusage __user *ru)
 {
        unsigned long state;
        int retval;
+       int status;
+
+       if (unlikely(noreap)) {
+               pid_t pid = p->pid;
+               uid_t uid = p->uid;
+               int exit_code = p->exit_code;
+               int why, status;
+
+               if (unlikely(p->state != TASK_ZOMBIE))
+                       return 0;
+               if (unlikely(p->exit_signal == -1 && p->ptrace == 0))
+                       return 0;
+               get_task_struct(p);
+               read_unlock(&tasklist_lock);
+               if ((exit_code & 0x7f) == 0) {
+                       why = CLD_EXITED;
+                       status = exit_code >> 8;
+               } else {
+                       why = (exit_code & 0x80) ? CLD_DUMPED : CLD_KILLED;
+                       status = exit_code & 0x7f;
+               }
+               return wait_noreap_copyout(p, pid, uid, why, status, infop);
+       }
 
        /*
         * Try to move the task's state to DEAD
@@ -977,12 +1025,13 @@ static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct ru
                BUG_ON(state != TASK_DEAD);
                return 0;
        }
-       if (unlikely(p->exit_signal == -1 && p->ptrace == 0))
+       if (unlikely(p->exit_signal == -1 && p->ptrace == 0)) {
                /*
                 * This can only happen in a race with a ptraced thread
                 * dying on another processor.
                 */
                return 0;
+       }
 
        /*
         * Now we are sure this task is interesting, and no other
@@ -991,12 +1040,32 @@ static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct ru
        read_unlock(&tasklist_lock);
 
        retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
-       if (!retval && stat_addr) {
-               if (p->signal->group_exit)
-                       retval = put_user(p->signal->group_exit_code, stat_addr);
-               else
-                       retval = put_user(p->exit_code, stat_addr);
+       status = p->signal->group_exit
+               ? p->signal->group_exit_code : p->exit_code;
+       if (!retval && stat_addr)
+               retval = put_user(status, stat_addr);
+       if (!retval && infop)
+               retval = put_user(SIGCHLD, &infop->si_signo);
+       if (!retval && infop)
+               retval = put_user(0, &infop->si_errno);
+       if (!retval && infop) {
+               int why;
+
+               if ((status & 0x7f) == 0) {
+                       why = CLD_EXITED;
+                       status >>= 8;
+               } else {
+                       why = (status & 0x80) ? CLD_DUMPED : CLD_KILLED;
+                       status &= 0x7f;
+               }
+               retval = put_user((short)why, &infop->si_code);
+               if (!retval)
+                       retval = put_user(status, &infop->si_status);
        }
+       if (!retval && infop)
+               retval = put_user(p->pid, &infop->si_pid);
+       if (!retval && infop)
+               retval = put_user(p->uid, &infop->si_uid);
        if (retval) {
                p->state = TASK_ZOMBIE;
                return retval;
@@ -1009,8 +1078,9 @@ static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct ru
                        __ptrace_unlink(p);
                        p->state = TASK_ZOMBIE;
                        /*
-                        * If this is not a detached task, notify the parent.  If it's
-                        * still not detached after that, don't release it now.
+                        * If this is not a detached task, notify the parent.
+                        * If it's still not detached after that, don't release
+                        * it now.
                         */
                        if (p->exit_signal != -1) {
                                do_notify_parent(p, p->exit_signal);
@@ -1032,9 +1102,9 @@ static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct ru
  * the lock and this task is uninteresting.  If we return nonzero, we have
  * released the lock and the system call should return.
  */
-static int wait_task_stopped(task_t *p, int delayed_group_leader,
-                            unsigned int __user *stat_addr,
-                            struct rusage __user *ru)
+static int wait_task_stopped(task_t *p, int delayed_group_leader, int noreap,
+                            struct siginfo __user *infop,
+                            int __user *stat_addr, struct rusage __user *ru)
 {
        int retval, exit_code;
 
@@ -1057,6 +1127,21 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader,
         */
        get_task_struct(p);
        read_unlock(&tasklist_lock);
+
+       if (unlikely(noreap)) {
+               pid_t pid = p->pid;
+               uid_t uid = p->uid;
+               int why = (p->ptrace & PT_PTRACED) ? CLD_TRAPPED : CLD_STOPPED;
+
+               exit_code = p->exit_code;
+               if (unlikely(!exit_code) ||
+                   unlikely(p->state > TASK_STOPPED))
+                       goto bail_ref;
+               return wait_noreap_copyout(p, pid, uid,
+                                          why, (exit_code << 8) | 0x7f,
+                                          infop);
+       }
+
        write_lock_irq(&tasklist_lock);
 
        /*
@@ -1082,6 +1167,7 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader,
                 * resumed, or it resumed and then died.
                 */
                write_unlock_irq(&tasklist_lock);
+bail_ref:
                put_task_struct(p);
                read_lock(&tasklist_lock);
                return 0;
@@ -1096,6 +1182,20 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader,
        retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
        if (!retval && stat_addr)
                retval = put_user((exit_code << 8) | 0x7f, stat_addr);
+       if (!retval && infop)
+               retval = put_user(SIGCHLD, &infop->si_signo);
+       if (!retval && infop)
+               retval = put_user(0, &infop->si_errno);
+       if (!retval && infop)
+               retval = put_user((short)((p->ptrace & PT_PTRACED)
+                                         ? CLD_TRAPPED : CLD_STOPPED),
+                                 &infop->si_code);
+       if (!retval && infop)
+               retval = put_user(exit_code, &infop->si_status);
+       if (!retval && infop)
+               retval = put_user(p->pid, &infop->si_pid);
+       if (!retval && infop)
+               retval = put_user(p->uid, &infop->si_uid);
        if (!retval)
                retval = p->pid;
        put_task_struct(p);
@@ -1104,15 +1204,13 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader,
        return retval;
 }
 
-asmlinkage long sys_wait4(pid_t pid,unsigned int __user *stat_addr, int options, struct rusage __user *ru)
+static long do_wait(pid_t pid, int options, struct siginfo __user *infop,
+                   int __user *stat_addr, struct rusage __user *ru)
 {
        DECLARE_WAITQUEUE(wait, current);
        struct task_struct *tsk;
        int flag, retval;
 
-       if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL))
-               return -EINVAL;
-
        add_wait_queue(&current->wait_chldexit,&wait);
 repeat:
        flag = 0;
@@ -1138,25 +1236,60 @@ repeat:
                                    !(p->ptrace & PT_PTRACED))
                                        continue;
                                retval = wait_task_stopped(p, ret == 2,
+                                                          (options & WNOWAIT),
+                                                          infop,
                                                           stat_addr, ru);
                                if (retval != 0) /* He released the lock.  */
-                                       goto end_wait4;
+                                       goto end;
                                break;
                        case TASK_ZOMBIE:
                                /*
                                 * Eligible but we cannot release it yet:
                                 */
                                if (ret == 2)
+                                       goto check_continued;
+                               if (!likely(options & WEXITED))
                                        continue;
-                               retval = wait_task_zombie(p, stat_addr, ru);
+                               retval = wait_task_zombie(
+                                       p, (options & WNOWAIT),
+                                       infop, stat_addr, ru);
                                if (retval != 0) /* He released the lock.  */
-                                       goto end_wait4;
+                                       goto end;
+                               break;
+                       case TASK_DEAD:
+                               continue;
+                       default:
+check_continued:
+                               if (!unlikely(options & WCONTINUED))
+                                       continue;
+                               if (unlikely(!p->signal))
+                                       continue;
+                               spin_lock_irq(&p->sighand->siglock);
+                               if (p->signal->stop_state < 0) {
+                                       pid_t pid;
+                                       uid_t uid;
+
+                                       if (!(options & WNOWAIT))
+                                               p->signal->stop_state = 0;
+                                       spin_unlock_irq(&p->sighand->siglock);
+                                       pid = p->pid;
+                                       uid = p->uid;
+                                       get_task_struct(p);
+                                       read_unlock(&tasklist_lock);
+                                       retval = wait_noreap_copyout(p, pid,
+                                                       uid, CLD_CONTINUED,
+                                                       SIGCONT, infop);
+                                       BUG_ON(retval == 0);
+                                       goto end;
+                               }
+                               spin_unlock_irq(&p->sighand->siglock);
                                break;
                        }
                }
                if (!flag) {
-                       list_for_each (_p,&tsk->ptrace_children) {
-                               p = list_entry(_p,struct task_struct,ptrace_list);
+                       list_for_each(_p, &tsk->ptrace_children) {
+                               p = list_entry(_p, struct task_struct,
+                                               ptrace_list);
                                if (!eligible_child(pid, options, p))
                                        continue;
                                flag = 1;
@@ -1169,24 +1302,84 @@ repeat:
                if (tsk->signal != current->signal)
                        BUG();
        } while (tsk != current);
+
        read_unlock(&tasklist_lock);
        if (flag) {
                retval = 0;
                if (options & WNOHANG)
-                       goto end_wait4;
+                       goto end;
                retval = -ERESTARTSYS;
                if (signal_pending(current))
-                       goto end_wait4;
+                       goto end;
                schedule();
                goto repeat;
        }
        retval = -ECHILD;
-end_wait4:
+end:
        current->state = TASK_RUNNING;
        remove_wait_queue(&current->wait_chldexit,&wait);
+       if (infop) {
+               if (retval > 0)
+               retval = 0;
+               else {
+                       /*
+                        * For a WNOHANG return, clear out all the fields
+                        * we would set so the user can easily tell the
+                        * difference.
+                        */
+                       if (!retval)
+                               retval = put_user(0, &infop->si_signo);
+                       if (!retval)
+                               retval = put_user(0, &infop->si_errno);
+                       if (!retval)
+                               retval = put_user(0, &infop->si_code);
+                       if (!retval)
+                               retval = put_user(0, &infop->si_pid);
+                       if (!retval)
+                               retval = put_user(0, &infop->si_uid);
+                       if (!retval)
+                               retval = put_user(0, &infop->si_status);
+               }
+       }
        return retval;
 }
 
+asmlinkage long sys_waitid(int which, pid_t pid,
+                          struct siginfo __user *infop, int options)
+{
+       if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED))
+               return -EINVAL;
+       if (!(options & (WEXITED|WSTOPPED|WCONTINUED)))
+               return -EINVAL;
+
+       switch (which) {
+       case P_ALL:
+               pid = -1;
+               break;
+       case P_PID:
+               if (pid <= 0)
+                       return -EINVAL;
+               break;
+       case P_PGID:
+               if (pid <= 0)
+                       return -EINVAL;
+               pid = -pid;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return do_wait(pid, options, infop, NULL, &infop->si_rusage);
+}
+
+asmlinkage long sys_wait4(pid_t pid, unsigned int *stat_addr,
+                               int options, struct rusage *ru)
+{
+       if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL))
+               return -EINVAL;
+       return do_wait(pid, options | WEXITED, NULL, stat_addr, ru);
+}
+
 #ifdef __ARCH_WANT_SYS_WAITPID
 
 /*
index e5d6cbc50c1e26dc32732b153cb7cb9069dd3314..300912082f997d024f03a8f8ebc52c188141a335 100644 (file)
@@ -26,6 +26,8 @@
 #include <asm/unistd.h>
 #include <asm/siginfo.h>
 
+extern void k_getrusage(struct task_struct *, int, struct rusage *);
+
 /*
  * SLAB caches for signal bits.
  */
@@ -660,6 +662,7 @@ static void handle_stop_signal(int sig, struct task_struct *p)
                         * the SIGCHLD was pending on entry to this kill.
                         */
                        p->signal->group_stop_count = 0;
+                       p->signal->stop_state = 1;
                        if (p->ptrace & PT_PTRACED)
                                do_notify_parent_cldstop(p, p->parent);
                        else
@@ -696,6 +699,21 @@ static void handle_stop_signal(int sig, struct task_struct *p)
 
                        t = next_thread(t);
                } while (t != p);
+
+               if (p->signal->stop_state > 0) {
+                       /*
+                        * We were in fact stopped, and are now continued.
+                        * Notify the parent with CLD_CONTINUED.
+                        */
+                       p->signal->stop_state = -1;
+                       p->signal->group_exit_code = 0;
+                       if (p->ptrace & PT_PTRACED)
+                               do_notify_parent_cldstop(p, p->parent);
+                       else
+                               do_notify_parent_cldstop(
+                                       p->group_leader,
+                                       p->group_leader->real_parent);
+               }
        }
 }
 
@@ -1466,6 +1484,7 @@ void do_notify_parent(struct task_struct *tsk, int sig)
        /* FIXME: find out whether or not this is supposed to be c*time. */
        info.si_utime = tsk->utime;
        info.si_stime = tsk->stime;
+       k_getrusage(tsk, RUSAGE_BOTH, &info.si_rusage);
 
        status = tsk->exit_code & 0x7f;
        why = SI_KERNEL;        /* shouldn't happen */
@@ -1555,9 +1574,16 @@ do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent)
        /* FIXME: find out whether or not this is supposed to be c*time. */
        info.si_utime = tsk->utime;
        info.si_stime = tsk->stime;
+       k_getrusage(tsk, RUSAGE_BOTH, &info.si_rusage);
 
-       info.si_status = tsk->exit_code & 0x7f;
-       info.si_code = CLD_STOPPED;
+       info.si_status = (tsk->signal ? tsk->signal->group_exit_code :
+                         tsk->exit_code) & 0x7f;
+       if (info.si_status == 0) {
+               info.si_status = SIGCONT;
+               info.si_code = CLD_CONTINUED;
+       } else {
+               info.si_code = CLD_STOPPED;
+       }
 
        sighand = parent->sighand;
        spin_lock_irqsave(&sighand->siglock, flags);
@@ -1623,14 +1649,17 @@ do_signal_stop(int signr)
                stop_count = --sig->group_stop_count;
                current->exit_code = signr;
                set_current_state(TASK_STOPPED);
+               if (stop_count == 0)
+                       sig->stop_state = 1;
                spin_unlock_irq(&sighand->siglock);
        }
        else if (thread_group_empty(current)) {
                /*
                 * Lock must be held through transition to stopped state.
                 */
-               current->exit_code = signr;
+               current->exit_code = current->signal->group_exit_code = signr;
                set_current_state(TASK_STOPPED);
+               sig->stop_state = 1;
                spin_unlock_irq(&sighand->siglock);
        }
        else {
@@ -1696,6 +1725,8 @@ do_signal_stop(int signr)
 
                current->exit_code = signr;
                set_current_state(TASK_STOPPED);
+               if (stop_count == 0)
+                       sig->stop_state = 1;
 
                spin_unlock_irq(&sighand->siglock);
                read_unlock(&tasklist_lock);
@@ -1736,6 +1767,8 @@ static inline int handle_group_stop(void)
         * without any associated signal being in our queue.
         */
        stop_count = --current->signal->group_stop_count;
+       if (stop_count == 0)
+               current->signal->stop_state = 1;
        current->exit_code = current->signal->group_exit_code;
        set_current_state(TASK_STOPPED);
        spin_unlock_irq(&current->sighand->siglock);
@@ -2098,6 +2131,8 @@ int copy_siginfo_to_user(siginfo_t __user *to, siginfo_t *from)
                err |= __put_user(from->si_status, &to->si_status);
                err |= __put_user(from->si_utime, &to->si_utime);
                err |= __put_user(from->si_stime, &to->si_stime);
+               err |= __copy_to_user(&to->si_rusage, &from->si_rusage,
+                                     sizeof(to->si_rusage));
                break;
        case __SI_RT: /* This is not generated by the kernel as of now. */
        case __SI_MESGQ: /* But this is */
index 1bbc66a60b8ccd53ac0080f10079f83ba01ed54f..28c5555e6352c08dfdf01db9d2d2562807bc0d32 100644 (file)
@@ -1540,37 +1540,43 @@ asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit __user *rlim)
  * reaped till shortly after the call to getrusage(), in both cases the
  * task being examined is in a frozen state so the counters won't change.
  */
-int getrusage(struct task_struct *p, int who, struct rusage __user *ru)
-{
-       struct rusage r;
 
-       memset((char *) &r, 0, sizeof(r));
+
+void k_getrusage(struct task_struct *p, int who, struct rusage *r)
+{
+       memset((char *) r, 0, sizeof *r);
        switch (who) {
                case RUSAGE_SELF:
-                       jiffies_to_timeval(p->utime, &r.ru_utime);
-                       jiffies_to_timeval(p->stime, &r.ru_stime);
-                       r.ru_nvcsw = p->nvcsw;
-                       r.ru_nivcsw = p->nivcsw;
-                       r.ru_minflt = p->min_flt;
-                       r.ru_majflt = p->maj_flt;
+                       jiffies_to_timeval(p->utime, &r->ru_utime);
+                       jiffies_to_timeval(p->stime, &r->ru_stime);
+                       r->ru_nvcsw = p->nvcsw;
+                       r->ru_nivcsw = p->nivcsw;
+                       r->ru_minflt = p->min_flt;
+                       r->ru_majflt = p->maj_flt;
                        break;
                case RUSAGE_CHILDREN:
-                       jiffies_to_timeval(p->cutime, &r.ru_utime);
-                       jiffies_to_timeval(p->cstime, &r.ru_stime);
-                       r.ru_nvcsw = p->cnvcsw;
-                       r.ru_nivcsw = p->cnivcsw;
-                       r.ru_minflt = p->cmin_flt;
-                       r.ru_majflt = p->cmaj_flt;
+                       jiffies_to_timeval(p->cutime, &r->ru_utime);
+                       jiffies_to_timeval(p->cstime, &r->ru_stime);
+                       r->ru_nvcsw = p->cnvcsw;
+                       r->ru_nivcsw = p->cnivcsw;
+                       r->ru_minflt = p->cmin_flt;
+                       r->ru_majflt = p->cmaj_flt;
                        break;
                default:
-                       jiffies_to_timeval(p->utime + p->cutime, &r.ru_utime);
-                       jiffies_to_timeval(p->stime + p->cstime, &r.ru_stime);
-                       r.ru_nvcsw = p->nvcsw + p->cnvcsw;
-                       r.ru_nivcsw = p->nivcsw + p->cnivcsw;
-                       r.ru_minflt = p->min_flt + p->cmin_flt;
-                       r.ru_majflt = p->maj_flt + p->cmaj_flt;
+                       jiffies_to_timeval(p->utime + p->cutime, &r->ru_utime);
+                       jiffies_to_timeval(p->stime + p->cstime, &r->ru_stime);
+                       r->ru_nvcsw = p->nvcsw + p->cnvcsw;
+                       r->ru_nivcsw = p->nivcsw + p->cnivcsw;
+                       r->ru_minflt = p->min_flt + p->cmin_flt;
+                       r->ru_majflt = p->maj_flt + p->cmaj_flt;
                        break;
        }
+}
+
+int getrusage(struct task_struct *p, int who, struct rusage __user *ru)
+{
+       struct rusage r;
+       k_getrusage(p, who, &r);
        return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;
 }