diff options
| author | Daniel Jacobowitz <drow@nevyn.them.org> | 2003-02-07 06:39:09 -0500 |
|---|---|---|
| committer | Daniel Jacobowitz <drow@nevyn.them.org> | 2003-02-07 06:39:09 -0500 |
| commit | 687bc18d6aec8958d690cde01ce7dca2fcc8f871 (patch) | |
| tree | a734f6bb03fc0a5b90925bf4d021915928ea0310 | |
| parent | 8eae299835cf161a93a5acd890cebf0f83f2a2ce (diff) | |
| parent | 88d9b86915c0c8091bc94811977c3806f228431f (diff) | |
Hand merge
| -rw-r--r-- | include/linux/ptrace.h | 6 | ||||
| -rw-r--r-- | include/linux/sched.h | 3 | ||||
| -rw-r--r-- | kernel/exit.c | 28 | ||||
| -rw-r--r-- | kernel/fork.c | 6 | ||||
| -rw-r--r-- | kernel/ptrace.c | 36 | ||||
| -rw-r--r-- | kernel/signal.c | 8 |
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; |
