diff options
| author | Linus Torvalds <torvalds@penguin.transmeta.com> | 2002-10-31 20:56:34 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2002-10-31 20:56:34 -0800 |
| commit | 32f06e3759ab748e9752b185a22c8a018e8e107a (patch) | |
| tree | 31b831355fec12899a10b302d27a8ed5b4cce90b /kernel | |
| parent | 63b367a492da0daedfcb0c9065e7a96f8cbda9fc (diff) | |
| parent | e7034436abd96604b8117aaa471d03bfd2d66c20 (diff) | |
Merge bk://nevyn.them.org:5000
into penguin.transmeta.com:/home/penguin/torvalds/repositories/kernel/linux
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/fork.c | 29 | ||||
| -rw-r--r-- | kernel/ptrace.c | 69 |
2 files changed, 98 insertions, 0 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index f183c1a7df17..90b273e7d8ec 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -946,6 +946,22 @@ bad_fork_free: goto fork_out; } +static inline int fork_traceflag (unsigned clone_flags) +{ + if (clone_flags & (CLONE_UNTRACED | CLONE_IDLETASK)) + return 0; + else if (clone_flags & CLONE_VFORK) { + if (current->ptrace & PT_TRACE_VFORK) + return PTRACE_EVENT_VFORK; + } else if ((clone_flags & CSIGNAL) != SIGCHLD) { + if (current->ptrace & PT_TRACE_CLONE) + return PTRACE_EVENT_CLONE; + } else if (current->ptrace & PT_TRACE_FORK) + return PTRACE_EVENT_FORK; + + return 0; +} + /* * Ok, this is the main fork-routine. * @@ -959,6 +975,13 @@ struct task_struct *do_fork(unsigned long clone_flags, int *user_tid) { struct task_struct *p; + int trace = 0; + + if (unlikely(current->ptrace)) { + trace = fork_traceflag (clone_flags); + if (trace) + clone_flags |= CLONE_PTRACE; + } p = copy_process(clone_flags, stack_start, regs, stack_size, user_tid); if (!IS_ERR(p)) { @@ -974,6 +997,12 @@ struct task_struct *do_fork(unsigned long clone_flags, wake_up_forked_process(p); /* do this last */ ++total_forks; + + if (unlikely (trace)) { + current->ptrace_message = (unsigned long) p->pid; + ptrace_notify ((trace << 8) | SIGTRAP); + } + if (clone_flags & CLONE_VFORK) wait_for_completion(&vfork); else diff --git a/kernel/ptrace.c b/kernel/ptrace.c index cc7a76bb65b7..a2611977e53b 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -248,3 +248,72 @@ int ptrace_writedata(struct task_struct *tsk, char * src, unsigned long dst, int } return copied; } + +static int ptrace_setoptions(struct task_struct *child, long data) +{ + if (data & PTRACE_O_TRACESYSGOOD) + child->ptrace |= PT_TRACESYSGOOD; + else + child->ptrace &= ~PT_TRACESYSGOOD; + + if (data & PTRACE_O_TRACEFORK) + child->ptrace |= PT_TRACE_FORK; + else + child->ptrace &= ~PT_TRACE_FORK; + + if (data & PTRACE_O_TRACEVFORK) + child->ptrace |= PT_TRACE_VFORK; + else + child->ptrace &= ~PT_TRACE_VFORK; + + if (data & PTRACE_O_TRACECLONE) + child->ptrace |= PT_TRACE_CLONE; + else + child->ptrace &= ~PT_TRACE_CLONE; + + if (data & PTRACE_O_TRACEEXEC) + child->ptrace |= PT_TRACE_EXEC; + else + child->ptrace &= ~PT_TRACE_EXEC; + + if ((data & (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK + | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE + | PTRACE_O_TRACEEXEC)) + != data) + return -EINVAL; + + return 0; +} + +int ptrace_request(struct task_struct *child, long request, + long addr, long data) +{ + int ret = -EIO; + + switch (request) { +#ifdef PTRACE_OLDSETOPTIONS + case PTRACE_OLDSETOPTIONS: +#endif + case PTRACE_SETOPTIONS: + ret = ptrace_setoptions(child, data); + break; + case PTRACE_GETEVENTMSG: + ret = put_user(child->ptrace_message, (unsigned long *) data); + break; + default: + break; + } + + return ret; +} + +void ptrace_notify(int exit_code) +{ + BUG_ON (!(current->ptrace & PT_PTRACED)); + + /* Let the debugger run. */ + current->exit_code = exit_code; + set_current_state(TASK_STOPPED); + notify_parent(current, SIGCHLD); + schedule(); +} |
