summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Jacobowitz <drow@nevyn.them.org>2003-02-07 06:39:09 -0500
committerDaniel Jacobowitz <drow@nevyn.them.org>2003-02-07 06:39:09 -0500
commit687bc18d6aec8958d690cde01ce7dca2fcc8f871 (patch)
treea734f6bb03fc0a5b90925bf4d021915928ea0310
parent8eae299835cf161a93a5acd890cebf0f83f2a2ce (diff)
parent88d9b86915c0c8091bc94811977c3806f228431f (diff)
Hand merge
-rw-r--r--include/linux/ptrace.h6
-rw-r--r--include/linux/sched.h3
-rw-r--r--kernel/exit.c28
-rw-r--r--kernel/fork.c6
-rw-r--r--kernel/ptrace.c36
-rw-r--r--kernel/signal.c8
6 files changed, 80 insertions, 7 deletions
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index c6de3a4ea70a..706b420fb5c9 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 78970007590f..ea5d949f946c 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 febad08ae9ef..fbc00cfae030 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 988a195bcc93..9e12b35e3924 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 a16dfb90d412..14d158864d9e 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 a095215cffb1..670141c149a7 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;