summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/kern/kern_exit.c121
-rw-r--r--sys/kern/kern_fork.c1
-rw-r--r--sys/kern/sys_process.c3
-rw-r--r--sys/sys/proc.h11
4 files changed, 96 insertions, 40 deletions
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index 49192bf..e098107 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -720,7 +720,6 @@ proc_reap(struct thread *td, struct proc *p, int *status, int options,
if (p->p_oppid && (t = pfind(p->p_oppid)) != NULL) {
PROC_LOCK(p);
proc_reparent(p, t);
- p->p_pptr->p_dbg_child--;
p->p_oppid = 0;
PROC_UNLOCK(p);
pksignal(t, SIGCHLD, p->p_ksi);
@@ -739,6 +738,10 @@ proc_reap(struct thread *td, struct proc *p, int *status, int options,
LIST_REMOVE(p, p_list); /* off zombproc */
sx_xunlock(&allproc_lock);
LIST_REMOVE(p, p_sibling);
+ if (p->p_flag & P_ORPHAN) {
+ LIST_REMOVE(p, p_orphan);
+ p->p_flag &= ~P_ORPHAN;
+ }
leavepgrp(p);
#ifdef PROCDESC
if (p->p_procdesc != NULL)
@@ -803,12 +806,53 @@ proc_reap(struct thread *td, struct proc *p, int *status, int options,
sx_xunlock(&allproc_lock);
}
+static int
+proc_to_reap(struct thread *td, struct proc *p, pid_t pid, int *status,
+ int options, struct rusage *rusage)
+{
+ struct proc *q;
+
+ q = td->td_proc;
+ PROC_LOCK(p);
+ if (pid != WAIT_ANY && p->p_pid != pid && p->p_pgid != -pid) {
+ PROC_UNLOCK(p);
+ return (0);
+ }
+ if (p_canwait(td, p)) {
+ 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
+ * functions need to be able to distinguish between waiting
+ * on a process and waiting on a thread. It is a thread if
+ * p_sigparent is not SIGCHLD, and the WLINUXCLONE option
+ * signifies we want to wait for threads and not processes.
+ */
+ if ((p->p_sigparent != SIGCHLD) ^
+ ((options & WLINUXCLONE) != 0)) {
+ PROC_UNLOCK(p);
+ return (0);
+ }
+
+ PROC_SLOCK(p);
+ if (p->p_state == PRS_ZOMBIE) {
+ proc_reap(td, p, status, options, rusage);
+ return (-1);
+ }
+ PROC_SUNLOCK(p);
+ PROC_UNLOCK(p);
+ return (1);
+}
+
int
kern_wait(struct thread *td, pid_t pid, int *status, int options,
struct rusage *rusage)
{
struct proc *p, *q;
- int error, nfound;
+ int error, nfound, ret;
AUDIT_ARG_PID(pid);
AUDIT_ARG_VALUE(options);
@@ -831,37 +875,16 @@ loop:
nfound = 0;
sx_xlock(&proctree_lock);
LIST_FOREACH(p, &q->p_children, p_sibling) {
- PROC_LOCK(p);
- if (pid != WAIT_ANY &&
- p->p_pid != pid && p->p_pgid != -pid) {
- PROC_UNLOCK(p);
- continue;
- }
- if (p_canwait(td, p)) {
- PROC_UNLOCK(p);
- continue;
- }
-
- /*
- * This special case handles a kthread spawned by linux_clone
- * (see linux_misc.c). The linux_wait4 and linux_waitpid
- * functions need to be able to distinguish between waiting
- * on a process and waiting on a thread. It is a thread if
- * p_sigparent is not SIGCHLD, and the WLINUXCLONE option
- * signifies we want to wait for threads and not processes.
- */
- if ((p->p_sigparent != SIGCHLD) ^
- ((options & WLINUXCLONE) != 0)) {
- PROC_UNLOCK(p);
+ ret = proc_to_reap(td, p, pid, status, options, rusage);
+ if (ret == 0)
continue;
- }
+ else if (ret == 1)
+ nfound++;
+ else
+ return (0);
- nfound++;
+ PROC_LOCK(p);
PROC_SLOCK(p);
- if (p->p_state == PRS_ZOMBIE) {
- proc_reap(td, p, status, options, rusage);
- return (0);
- }
if ((p->p_flag & P_STOPPED_SIG) &&
(p->p_suspcount == p->p_numthreads) &&
(p->p_flag & P_WAITED) == 0 &&
@@ -893,16 +916,34 @@ loop:
if (status)
*status = SIGCONT;
- return (0);
}
PROC_UNLOCK(p);
}
+
+ /*
+ * Look in the orphans list too, to allow the parent to
+ * collect it's child exit status even if child is being
+ * debugged.
+ *
+ * Debugger detaches from the parent upon successful
+ * switch-over from parent to child. At this point due to
+ * re-parenting the parent loses the child to debugger and a
+ * wait4(2) call would report that it has no children to wait
+ * for. By maintaining a list of orphans we allow the parent
+ * 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);
+ if (ret == 0)
+ continue;
+ else if (ret == 1)
+ nfound++;
+ else
+ return (0);
+ }
if (nfound == 0) {
sx_xunlock(&proctree_lock);
- if (td->td_proc->p_dbg_child)
- return (0);
- else
- return (ECHILD);
+ return (ECHILD);
}
if (options & WNOHANG) {
sx_xunlock(&proctree_lock);
@@ -940,5 +981,15 @@ proc_reparent(struct proc *child, struct proc *parent)
PROC_UNLOCK(child->p_pptr);
LIST_REMOVE(child, p_sibling);
LIST_INSERT_HEAD(&parent->p_children, child, p_sibling);
+
+ if (child->p_flag & P_ORPHAN) {
+ LIST_REMOVE(child, p_orphan);
+ child->p_flag &= ~P_ORPHAN;
+ }
+ if (child->p_flag & P_TRACED) {
+ LIST_INSERT_HEAD(&child->p_pptr->p_orphans, child, p_orphan);
+ child->p_flag |= P_ORPHAN;
+ }
+
child->p_pptr = parent;
}
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index e447c93..2678533 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -590,6 +590,7 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2,
LIST_INSERT_AFTER(p1, p2, p_pglist);
PGRP_UNLOCK(p1->p_pgrp);
LIST_INIT(&p2->p_children);
+ LIST_INIT(&p2->p_orphans);
callout_init(&p2->p_itcallout, CALLOUT_MPSAFE);
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index 2060efe..9c6c204 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -841,8 +841,6 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
p->p_flag |= P_TRACED;
p->p_oppid = p->p_pptr->p_pid;
if (p->p_pptr != td->td_proc) {
- /* Remember that a child is being debugged(traced). */
- p->p_pptr->p_dbg_child++;
proc_reparent(p, td->td_proc);
}
data = SIGSTOP;
@@ -931,7 +929,6 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
PROC_UNLOCK(pp);
PROC_LOCK(p);
proc_reparent(p, pp);
- p->p_pptr->p_dbg_child--;
if (pp == initproc)
p->p_sigparent = SIGCHLD;
}
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 0245e88..da71b40 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -507,8 +507,6 @@ struct proc {
/* The following fields are all zeroed upon creation in fork. */
#define p_startzero p_oppid
pid_t p_oppid; /* (c + e) Save ppid in ptrace. XXX */
- int p_dbg_child; /* (c + e) # of debugged children in
- ptrace. */
struct vmspace *p_vmspace; /* (b) Address space. */
u_int p_swtick; /* (c) Tick when swapped in or out. */
struct itimerval p_realtimer; /* (c) Alarm timer. */
@@ -576,6 +574,14 @@ struct proc {
after fork. */
uint64_t p_prev_runtime; /* (c) Resource usage accounting. */
struct racct *p_racct; /* (b) Resource accounting. */
+ /*
+ * An orphan is the child that has beed re-parented to the
+ * debugger as a result of attaching to it. Need to keep
+ * track of them for parent to be able to collect the exit
+ * status of what used to be children.
+ */
+ LIST_ENTRY(proc) p_orphan; /* (e) List of orphan processes. */
+ LIST_HEAD(, proc) p_orphans; /* (e) Pointer to list of orphans. */
};
#define p_session p_pgrp->pg_session
@@ -614,6 +620,7 @@ struct proc {
#define P_HWPMC 0x800000 /* Process is using HWPMCs */
#define P_JAILED 0x1000000 /* Process is in jail. */
+#define P_ORPHAN 0x2000000 /* Orphaned. */
#define P_INEXEC 0x4000000 /* Process is in execve(). */
#define P_STATCHILD 0x8000000 /* Child process stopped or exited. */
#define P_INMEM 0x10000000 /* Loaded into memory. */
OpenPOWER on IntegriCloud