Preliminary ptrace support.

diff --git a/server/thread.c b/server/thread.c
index 5529733..11cb1b9 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -15,8 +15,10 @@
 #ifdef HAVE_SYS_MMAN_H
 #include <sys/mman.h>
 #endif
+#include <sys/ptrace.h>
 #include <sys/types.h>
 #include <sys/uio.h>
+#include <sys/wait.h>
 #include <unistd.h>
 #include <stdarg.h>
 
@@ -118,7 +120,8 @@
     thread->apc         = NULL;
     thread->apc_count   = 0;
     thread->error       = 0;
-    thread->state       = STARTING;
+    thread->state       = RUNNING;
+    thread->attached    = 0;
     thread->exit_code   = 0x103;  /* STILL_ACTIVE */
     thread->next        = NULL;
     thread->prev        = NULL;
@@ -223,16 +226,118 @@
     }
 }
 
+/* find a thread from a Unix pid */
+static struct thread *get_thread_from_pid( int pid )
+{
+    struct thread *t = first_thread;
+    while (t && (t->unix_pid != pid)) t = t->next;
+    return t;
+}
+
+/* wait for a ptraced child to get a certain signal */
+/* if the signal is 0, we simply check if anything is pending and return at once */
+void wait4_thread( struct thread *thread, int signal )
+{
+    int status;
+    int pid;
+
+
+ restart:
+    pid = thread ? thread->unix_pid : -1;
+    if ((pid = wait4( pid, &status, WUNTRACED | (signal ? 0 : WNOHANG), NULL )) == -1)
+    {
+        perror( "wait4" );
+        return;
+    }
+    if (WIFSTOPPED(status))
+    {
+        int sig = WSTOPSIG(status);
+        if (debug_level) fprintf( stderr, "ptrace: pid %d got sig %d\n", pid, sig );
+        switch(sig)
+        {
+        case SIGSTOP:  /* continue at once if not suspended */
+            if (!thread) thread = get_thread_from_pid( pid );
+            if (!(thread->process->suspend + thread->suspend))
+                ptrace( PTRACE_CONT, pid, 0, sig );
+            break;
+        default:  /* ignore other signals for now */
+            ptrace( PTRACE_CONT, pid, 0, sig );
+            break;
+        }
+        if (signal && sig != signal) goto restart;
+    }
+    else if (WIFSIGNALED(status))
+    {
+        int exit_code = WTERMSIG(status);
+        if (debug_level)
+            fprintf( stderr, "ptrace: pid %d killed by sig %d\n", pid, exit_code );
+        if (!thread) thread = get_thread_from_pid( pid );
+        if (thread->client) remove_client( thread->client, exit_code );
+    }
+    else if (WIFEXITED(status))
+    {
+        int exit_code = WEXITSTATUS(status);
+        if (debug_level)
+            fprintf( stderr, "ptrace: pid %d exited with status %d\n", pid, exit_code );
+        if (!thread) thread = get_thread_from_pid( pid );
+        if (thread->client) remove_client( thread->client, exit_code );
+    }
+    else fprintf( stderr, "wait4: pid %d unknown status %x\n", pid, status );
+}
+
+/* attach to a Unix thread */
+static int attach_thread( struct thread *thread )
+{
+    /* this may fail if the client is already being debugged */
+    if (ptrace( PTRACE_ATTACH, thread->unix_pid, 0, 0 ) == -1) return 0;
+    if (debug_level) fprintf( stderr, "ptrace: attached to pid %d\n", thread->unix_pid );
+    thread->attached = 1;
+    wait4_thread( thread, SIGSTOP );
+    return 1;
+}
+
+/* detach from a Unix thread and kill it */
+static void detach_thread( struct thread *thread )
+{
+    if (!thread->unix_pid) return;
+    kill( thread->unix_pid, SIGTERM );
+    if (thread->suspend + thread->process->suspend) continue_thread( thread );
+    if (thread->attached)
+    {
+        wait4_thread( thread, SIGTERM );
+        if (debug_level) fprintf( stderr, "ptrace: detaching from %d\n", thread->unix_pid );
+        ptrace( PTRACE_DETACH, thread->unix_pid, 0, SIGTERM );
+        thread->attached = 0;
+    }
+}
+
+/* stop a thread (at the Unix level) */
+void stop_thread( struct thread *thread )
+{
+    if (!thread->unix_pid) return;
+    /* first try to attach to it */
+    if (!thread->attached)
+        if (attach_thread( thread )) return;  /* this will have stopped it */
+    /* attached already, or attach failed -> send a signal */
+    kill( thread->unix_pid, SIGSTOP );
+    if (thread->attached) wait4_thread( thread, SIGSTOP );
+}
+
+/* make a thread continue (at the Unix level) */
+void continue_thread( struct thread *thread )
+{
+    if (!thread->unix_pid) return;
+    if (!thread->attached) kill( thread->unix_pid, SIGCONT );
+    else ptrace( PTRACE_CONT, thread->unix_pid, 0, SIGSTOP );
+}
+ 
 /* suspend a thread */
 static int suspend_thread( struct thread *thread )
 {
     int old_count = thread->suspend;
     if (thread->suspend < MAXIMUM_SUSPEND_COUNT)
     {
-        if (!(thread->process->suspend + thread->suspend++))
-        {
-            if (thread->unix_pid) kill( thread->unix_pid, SIGSTOP );
-        }
+        if (!(thread->process->suspend + thread->suspend++)) stop_thread( thread );
     }
     return old_count;
 }
@@ -243,10 +348,7 @@
     int old_count = thread->suspend;
     if (thread->suspend > 0)
     {
-        if (!(--thread->suspend + thread->process->suspend))
-        {
-            if (thread->unix_pid) kill( thread->unix_pid, SIGCONT );
-        }
+        if (!(--thread->suspend + thread->process->suspend)) continue_thread( thread );
     }
     return old_count;
 }
@@ -493,7 +595,6 @@
 void kill_thread( struct thread *thread, int exit_code )
 {
     if (thread->state == TERMINATED) return;  /* already killed */
-    if (thread->unix_pid) kill( thread->unix_pid, SIGTERM );
     remove_client( thread->client, exit_code ); /* this will call thread_killed */
 }
 
@@ -502,11 +603,13 @@
 {
     thread->state = TERMINATED;
     thread->exit_code = exit_code;
+    thread->client = NULL;
     if (thread->wait) end_wait( thread );
     debug_exit_thread( thread, exit_code );
     abandon_mutexes( thread );
     remove_process_thread( thread->process, thread );
     wake_up( &thread->obj, 0 );
+    detach_thread( thread );
     release_object( thread );
 }
 
@@ -544,16 +647,14 @@
 /* initialize a new thread */
 DECL_HANDLER(init_thread)
 {
-    if (current->state != STARTING)
+    if (current->unix_pid)
     {
         fatal_protocol_error( current, "init_thread: already running\n" );
         return;
     }
-    current->state    = RUNNING;
     current->unix_pid = req->unix_pid;
     current->teb      = req->teb;
-    if (current->suspend + current->process->suspend > 0)
-        kill( current->unix_pid, SIGSTOP );
+    if (current->suspend + current->process->suspend > 0) stop_thread( current );
     req->pid = current->process;
     req->tid = current;
 }