diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index c6de3a4ea70a5febf8dc802aed6e73e996d64e8d..706b420fb5c9cc9a43707576af4b53524eea30f2 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -26,6 +26,8 @@
 /* 0x4200-0x4300 are reserved for architecture-independent additions.  */
 #define PTRACE_SETOPTIONS	0x4200
 #define PTRACE_GETEVENTMSG	0x4201
+#define PTRACE_GETSIGINFO	0x4202
+#define PTRACE_SETSIGINFO	0x4203
 
 /* options set using PTRACE_SETOPTIONS */
 #define PTRACE_O_TRACESYSGOOD	0x00000001
@@ -33,12 +35,16 @@
 #define PTRACE_O_TRACEVFORK	0x00000004
 #define PTRACE_O_TRACECLONE	0x00000008
 #define PTRACE_O_TRACEEXEC	0x00000010
+#define PTRACE_O_TRACEVFORKDONE	0x00000020
+#define PTRACE_O_TRACEEXIT	0x00000040
 
 /* Wait extended result codes for the above trace options.  */
 #define PTRACE_EVENT_FORK	1
 #define PTRACE_EVENT_VFORK	2
 #define PTRACE_EVENT_CLONE	3
 #define PTRACE_EVENT_EXEC	4
+#define PTRACE_EVENT_VFORK_DONE	5
+#define PTRACE_EVENT_EXIT	6
 
 #include <asm/ptrace.h>
 #include <linux/sched.h>
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 78970007590fb70270850539aa0aa57acb4a579d..ea5d949f946cc1b5c9bc6fa9fa9b6452f6838e93 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -417,6 +417,7 @@ struct task_struct {
 	struct backing_dev_info *backing_dev_info;
 
 	unsigned long ptrace_message;
+	siginfo_t *last_siginfo; /* For ptrace use.  */
 };
 
 extern void __put_task_struct(struct task_struct *tsk);
@@ -457,6 +458,8 @@ do { if (atomic_dec_and_test(&(tsk)->usage)) __put_task_struct(tsk); } while(0)
 #define PT_TRACE_VFORK	0x00000020
 #define PT_TRACE_CLONE	0x00000040
 #define PT_TRACE_EXEC	0x00000080
+#define PT_TRACE_VFORK_DONE	0x00000100
+#define PT_TRACE_EXIT	0x00000200
 
 #if CONFIG_SMP
 extern void set_cpus_allowed(task_t *p, unsigned long new_mask);
diff --git a/kernel/exit.c b/kernel/exit.c
index febad08ae9ef531706cffe074d010aaa07c785cd..fbc00cfae0304345b5e5ce41a2651baeb9b98587 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -199,6 +199,17 @@ static inline int has_stopped_jobs(int pgrp)
 	for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) {
 		if (p->state != TASK_STOPPED)
 			continue;
+
+		/* If p is stopped by a debugger on a signal that won't
+		   stop it, then don't count p as stopped.  This isn't
+		   perfect but it's a good approximation.  */
+		if (unlikely (p->ptrace)
+		    && p->exit_code != SIGSTOP
+		    && p->exit_code != SIGTSTP
+		    && p->exit_code != SIGTTOU
+		    && p->exit_code != SIGTTIN)
+			continue;
+
 		retval = 1;
 		break;
 	}
@@ -594,7 +605,7 @@ static void exit_notify(struct task_struct *tsk)
 	 * is about to become orphaned.
 	 */
 	 
-	t = tsk->parent;
+	t = tsk->real_parent;
 	
 	if ((t->pgrp != tsk->pgrp) &&
 	    (t->session == tsk->session) &&
@@ -627,8 +638,16 @@ static void exit_notify(struct task_struct *tsk)
 		tsk->exit_signal = SIGCHLD;
 
 
-	if (tsk->exit_signal != -1)
-		do_notify_parent(tsk, tsk->exit_signal);
+	/* If something other than our normal parent is ptracing us, then
+	 * send it a SIGCHLD instead of honoring exit_signal.  exit_signal
+	 * only has special meaning to our real parent.
+	 */
+	if (tsk->exit_signal != -1) {
+		if (tsk->parent == tsk->real_parent)
+			do_notify_parent(tsk, tsk->exit_signal);
+		else
+			do_notify_parent(tsk, SIGCHLD);
+	}
 
 	tsk->state = TASK_ZOMBIE;
 	/*
@@ -661,6 +680,9 @@ NORET_TYPE void do_exit(long code)
 
 	profile_exit_task(tsk);
  
+	if (unlikely(current->ptrace & PT_TRACE_EXIT))
+		ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP);
+
 	acct_process(code);
 	__exit_mm(tsk);
 
diff --git a/kernel/fork.c b/kernel/fork.c
index 988a195bcc932a26f9757a9791ab68b438edd6f5..9e12b35e39243b599bfdba76372be6c6b4a84e03 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1085,9 +1085,11 @@ struct task_struct *do_fork(unsigned long clone_flags,
 			ptrace_notify ((trace << 8) | SIGTRAP);
 		}
 
-		if (clone_flags & CLONE_VFORK)
+		if (clone_flags & CLONE_VFORK) {
 			wait_for_completion(&vfork);
-		else
+			if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE))
+				ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
+		} else
 			/*
 			 * Let the child process run first, to avoid most of the
 			 * COW overhead when the child exec()s afterwards.
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index a16dfb90d4121f9133e4334dfcfffa44f3ea100a..14d158864d9ea1ec8993e53fd04366d685faf29d 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -277,15 +277,43 @@ static int ptrace_setoptions(struct task_struct *child, long data)
 	else
 		child->ptrace &= ~PT_TRACE_EXEC;
 
+	if (data & PTRACE_O_TRACEVFORKDONE)
+		child->ptrace |= PT_TRACE_VFORK_DONE;
+	else
+		child->ptrace &= ~PT_TRACE_VFORK_DONE;
+
+	if (data & PTRACE_O_TRACEEXIT)
+		child->ptrace |= PT_TRACE_EXIT;
+	else
+		child->ptrace &= ~PT_TRACE_EXIT;
+
 	if ((data & (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK
 		    | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE
-		    | PTRACE_O_TRACEEXEC))
+		    | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT
+		    | PTRACE_O_TRACEVFORKDONE))
 	    != data)
 		return -EINVAL;
 
 	return 0;
 }
 
+static int ptrace_getsiginfo(struct task_struct *child, long data)
+{
+	if (child->last_siginfo == NULL)
+		return -EINVAL;
+	return copy_siginfo_to_user ((siginfo_t *) data, child->last_siginfo);
+}
+
+static int ptrace_setsiginfo(struct task_struct *child, long data)
+{
+	if (child->last_siginfo == NULL)
+		return -EINVAL;
+	if (copy_from_user (child->last_siginfo, (siginfo_t *) data,
+			    sizeof (siginfo_t)) != 0)
+		return -EFAULT;
+	return 0;
+}
+
 int ptrace_request(struct task_struct *child, long request,
 		   long addr, long data)
 {
@@ -301,6 +329,12 @@ int ptrace_request(struct task_struct *child, long request,
 	case PTRACE_GETEVENTMSG:
 		ret = put_user(child->ptrace_message, (unsigned long *) data);
 		break;
+	case PTRACE_GETSIGINFO:
+		ret = ptrace_getsiginfo(child, data);
+		break;
+	case PTRACE_SETSIGINFO:
+		ret = ptrace_setsiginfo(child, data);
+		break;
 	default:
 		break;
 	}
diff --git a/kernel/signal.c b/kernel/signal.c
index a095215cffb1041f7a627e7632a2637aa2f23f54..670141c149a7cec491c5c3fbda13d06e8320a7a3 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1427,17 +1427,23 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs)
 
 			/* Let the debugger run.  */
 			current->exit_code = signr;
+			current->last_siginfo = info;
 			set_current_state(TASK_STOPPED);
 			notify_parent(current, SIGCHLD);
 			schedule();
 
+			current->last_siginfo = NULL;
+
 			/* We're back.  Did the debugger cancel the sig?  */
 			signr = current->exit_code;
 			if (signr == 0)
 				continue;
 			current->exit_code = 0;
 
-			/* Update the siginfo structure.  Is this good?  */
+			/* Update the siginfo structure if the signal has
+			   changed.  If the debugger wanted something
+			   specific in the siginfo structure then it should
+			   have updated *info via PTRACE_SETSIGINFO.  */
 			if (signr != info->si_signo) {
 				info->si_signo = signr;
 				info->si_errno = 0;