Index: /neercs/trunk/src/mytrace.c
===================================================================
--- /neercs/trunk/src/mytrace.c	(revision 2515)
+++ /neercs/trunk/src/mytrace.c	(revision 2516)
@@ -39,5 +39,5 @@
 static int memcpy_into_target(struct mytrace *t,
                               long dest, char const *src, size_t n);
-static long remote_syscall(pid_t pid, long call,
+static long remote_syscall(struct mytrace *t, long call,
                            long arg1, long arg2, long arg3);
 #   if defined DEBUG
@@ -53,12 +53,4 @@
 #define SYSCALL_X86   0x80cd  /* CD 80 = int $0x80 */
 #define SYSCALL_AMD64 0x050fL /* 0F 05 = syscall */
-
-#define MYCALL_OPEN     0
-#define MYCALL_CLOSE    1
-#define MYCALL_WRITE    2
-#define MYCALL_DUP2     3
-#define MYCALL_SETPGID  4
-#define MYCALL_SETSID   5
-#define MYCALL_KILL     6
 
 #if defined __x86_64__
@@ -84,17 +76,31 @@
 #endif
 
+#define MYCALL_OPEN     0
+#define MYCALL_CLOSE    1
+#define MYCALL_WRITE    2
+#define MYCALL_DUP2     3
+#define MYCALL_SETPGID  4
+#define MYCALL_SETSID   5
+#define MYCALL_KILL     6
+#define MYCALL_FORK     7
+#define MYCALL_EXIT     8
+
 #if defined __x86_64__
+/* from unistd_32.h on an amd64 system */
+int syscalls32[] = { 5, 6, 4, 63, 57, 66, 37, 2, 1 };
+int syscalls64[] =
+#else
 int syscalls32[] =
-    { 5, 6, 4, 63, 57, 66, 37 }; /* from unistd_32.h on an amd64 system */
-int syscalls64[] =
-#else
-int syscalls32[] =
 #endif
     { SYS_open, SYS_close, SYS_write, SYS_dup2, SYS_setpgid, SYS_setsid,
-      SYS_kill };
+      SYS_kill, SYS_fork, SYS_exit };
+
+char const *syscallnames[] =
+    { "open", "close", "write", "dup2", "setpgid", "setsid", "kill", "fork",
+      "exit" };
 
 struct mytrace
 {
-    pid_t pid;
+    pid_t pid, child;
 };
 
@@ -103,4 +109,5 @@
 #if defined USE_GRAB
     struct mytrace *t;
+    int status;
 
     if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
@@ -109,10 +116,41 @@
         return NULL;
     }
-    waitpid(pid, NULL, 0);
+    if(waitpid(pid, &status, 0) < 0)
+    {
+        perror("waitpid");
+        return NULL;
+    }
+    if(!WIFSTOPPED(status))
+    {
+        fprintf(stderr, "traced process was not stopped\n");
+        ptrace(PTRACE_DETACH, pid, 0, 0);
+        return NULL;
+    }
 
     t = malloc(sizeof(struct mytrace));
     t->pid = pid;
+    t->child = 0;
 
     return t;
+#else
+    errno = ENOSYS;
+    return NULL;
+#endif
+}
+
+struct mytrace* mytrace_fork(struct mytrace *t)
+{
+#if defined USE_GRAB
+    struct mytrace *child;
+
+    ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEFORK);
+    remote_syscall(t, MYCALL_FORK, 0, 0, 0);
+    waitpid(t->child, NULL, 0);
+
+    child = malloc(sizeof(struct mytrace));
+    child->pid = t->child;
+    child->child = 0;
+
+    return child;
 #else
     errno = ENOSYS;
@@ -128,4 +166,14 @@
 
     return 0;
+#else
+    errno = ENOSYS;
+    return -1;
+#endif
+}
+
+long mytrace_getpid(struct mytrace *t)
+{
+#if defined USE_GRAB
+    return t->pid;
 #else
     errno = ENOSYS;
@@ -154,5 +202,5 @@
     memcpy_into_target(t, regs.RSP, path, size);
 
-    ret = remote_syscall(t->pid, MYCALL_OPEN, regs.RSP, O_RDWR, 0755);
+    ret = remote_syscall(t, MYCALL_OPEN, regs.RSP, O_RDWR, 0755);
 
     /* Restore the data */
@@ -175,5 +223,5 @@
 {
 #if defined USE_GRAB
-    return remote_syscall(t->pid, MYCALL_CLOSE, fd, 0, 0);
+    return remote_syscall(t, MYCALL_CLOSE, fd, 0, 0);
 #else
     errno = ENOSYS;
@@ -203,5 +251,5 @@
     memcpy_into_target(t, regs.RSP, data, len);
 
-    ret = remote_syscall(t->pid, MYCALL_WRITE, fd, regs.RSP, len);
+    ret = remote_syscall(t, MYCALL_WRITE, fd, regs.RSP, len);
 
     /* Restore the data */
@@ -224,5 +272,5 @@
 {
 #if defined USE_GRAB
-    return remote_syscall(t->pid, MYCALL_DUP2, oldfd, newfd, 0);
+    return remote_syscall(t, MYCALL_DUP2, oldfd, newfd, 0);
 #else
     errno = ENOSYS;
@@ -234,5 +282,5 @@
 {
 #if defined USE_GRAB
-    return remote_syscall(t->pid, MYCALL_SETPGID, pid, pgid, 0);
+    return remote_syscall(t, MYCALL_SETPGID, pid, pgid, 0);
 #else
     errno = ENOSYS;
@@ -244,5 +292,5 @@
 {
 #if defined USE_GRAB
-    return remote_syscall(t->pid, MYCALL_SETSID, 0, 0, 0);
+    return remote_syscall(t, MYCALL_SETSID, 0, 0, 0);
 #else
     errno = ENOSYS;
@@ -254,5 +302,16 @@
 {
 #if defined USE_GRAB
-    return remote_syscall(t->pid, MYCALL_KILL, pid, sig, 0);
+    return remote_syscall(t, MYCALL_KILL, pid, sig, 0);
+#else
+    errno = ENOSYS;
+    return -1;
+#endif
+}
+
+int mytrace_exit(struct mytrace *t, int status)
+{
+#if defined USE_GRAB
+    ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEEXIT);
+    return remote_syscall(t, MYCALL_EXIT, status, 0, 0);
 #else
     errno = ENOSYS;
@@ -333,6 +392,6 @@
 }
 
-static long remote_syscall(pid_t pid, long call,
-                            long arg1, long arg2, long arg3)
+static long remote_syscall(struct mytrace *t, long call,
+                           long arg1, long arg2, long arg3)
 {
     /* Method for remote syscall:
@@ -347,5 +406,15 @@
     int bits;
 
-print_registers(pid);
+    if(call < 0 || call >= (long)(sizeof(syscallnames)/sizeof(*syscallnames)))
+    {
+        fprintf(stderr, "unknown remote syscall %li\n", call);
+        return -1;
+    }
+
+    debug("remote syscall %s(%lu, %lu, %lu)",
+          syscallnames[call], arg1, arg2, arg3);
+
+    print_registers(t->pid);
+
 #if defined __x86_64__
     bits = 64;
@@ -356,5 +425,5 @@
     for(;;)
     {
-        if(ptrace(PTRACE_GETREGS, pid, NULL, &oldregs) < 0)
+        if(ptrace(PTRACE_GETREGS, t->pid, NULL, &oldregs) < 0)
         {
             fprintf(stderr, "PTRACE_GETREGS failed\n");
@@ -362,5 +431,5 @@
         }
 
-        oinst = ptrace(PTRACE_PEEKTEXT, pid, oldregs.RIP - 2, 0) & 0xffff;
+        oinst = ptrace(PTRACE_PEEKTEXT, t->pid, oldregs.RIP - 2, 0) & 0xffff;
 
 #if defined __x86_64__
@@ -377,20 +446,20 @@
 #endif
 
-        if(ptrace(PTRACE_SYSCALL, pid, NULL, 0) < 0)
+        if(ptrace(PTRACE_SYSCALL, t->pid, NULL, 0) < 0)
         {
             perror("ptrace_syscall (1)");
             return -1;
         }
-        waitpid(pid, NULL, 0);
-
-        if(ptrace(PTRACE_SYSCALL, pid, NULL, 0) < 0)
+        waitpid(t->pid, NULL, 0);
+
+        if(ptrace(PTRACE_SYSCALL, t->pid, NULL, 0) < 0)
         {
             perror("ptrace_syscall (2)");
             return -1;
         }
-        waitpid(pid, NULL, 0);
-    }
-
-    print_registers(pid);
+        waitpid(t->pid, NULL, 0);
+    }
+
+    print_registers(t->pid);
 
     regs = oldregs;
@@ -413,5 +482,5 @@
     }
 
-    if(ptrace(PTRACE_SETREGS, pid, NULL, &regs) < 0)
+    if(ptrace(PTRACE_SETREGS, t->pid, NULL, &regs) < 0)
     {
         fprintf(stderr, "PTRACE_SETREGS failed\n");
@@ -419,16 +488,44 @@
     }
 
-    print_registers(pid);
-
-    if(ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0)
-    {
-        fprintf(stderr, "PTRACE_SINGLESTEP failed\n");
-        return -1;
-    }
-    waitpid(pid, NULL, 0);
-
-    print_registers(pid);
-
-    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
+    for(;;)
+    {
+        int status;
+
+        print_registers(t->pid);
+
+        if(ptrace(PTRACE_SINGLESTEP, t->pid, NULL, NULL) < 0)
+        {
+            fprintf(stderr, "PTRACE_SINGLESTEP failed\n");
+            return -1;
+        }
+        waitpid(t->pid, &status, 0);
+
+        if(WIFEXITED(status))
+            return 0;
+
+        if(!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP)
+            continue;
+
+        /* Fuck Linux: there is no macro for this */
+        switch((status >> 16) & 0xffff)
+        {
+        case PTRACE_EVENT_FORK:
+            if(ptrace(PTRACE_GETEVENTMSG, t->pid, 0, &t->child) < 0)
+            {
+                fprintf(stderr, "PTRACE_GETEVENTMSG failed\n");
+                return -1;
+            }
+            continue;
+        case PTRACE_EVENT_EXIT:
+            /* The process is about to exit, don't do anything else */
+            return 0;
+        }
+
+        break;
+    }
+
+    print_registers(t->pid);
+
+    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
     {
         fprintf(stderr, "PTRACE_GETREGS failed\n");
@@ -436,12 +533,12 @@
     }
 
-    if(ptrace(PTRACE_SETREGS, pid, NULL, &oldregs) < 0)
+    if(ptrace(PTRACE_SETREGS, t->pid, NULL, &oldregs) < 0)
     {
         fprintf(stderr, "PTRACE_SETREGS failed\n");
         return -1;
     }
-    print_registers(pid);
-
-    debug("syscall %ld returned %ld", call, regs.RAX);
+    print_registers(t->pid);
+
+    debug("syscall %s returned %ld", syscallnames[call], regs.RAX);
 
     if((long)regs.RAX < 0)
Index: /neercs/trunk/src/mytrace.h
===================================================================
--- /neercs/trunk/src/mytrace.h	(revision 2515)
+++ /neercs/trunk/src/mytrace.h	(revision 2516)
@@ -16,5 +16,7 @@
 
 struct mytrace* mytrace_attach(long int pid);
+struct mytrace* mytrace_fork(struct mytrace *t);
 int mytrace_detach(struct mytrace *t);
+long mytrace_getpid(struct mytrace *t);
 
 int mytrace_open(struct mytrace *t, char const *path, int mode);
@@ -25,3 +27,4 @@
 int mytrace_setsid(struct mytrace *t);
 int mytrace_kill(struct mytrace *t, long pid, int sig);
+int mytrace_exit(struct mytrace *t, int status);
 
Index: /neercs/trunk/src/grab.c
===================================================================
--- /neercs/trunk/src/grab.c	(revision 2515)
+++ /neercs/trunk/src/grab.c	(revision 2516)
@@ -39,5 +39,5 @@
 #if defined HAVE_LINUX_KDEV_T_H
     char fdstr[1024];
-    struct mytrace *t;
+    struct mytrace *parent, *child;
     int i, fd = 0, mode, ret;
     char to_open[3];
@@ -46,10 +46,13 @@
     debug("pty is %s", ptyname);
 
-    t = mytrace_attach(pid);
-    if(!t)
+    parent = mytrace_attach(pid);
+    if(!parent)
     {
         fprintf(stderr, "Cannot access process %ld\n", pid);
         return -1;
     }
+
+    child = mytrace_fork(parent);
+    pid = mytrace_getpid(child);
 
     /* Look for file descriptors that are PTYs */
@@ -75,5 +78,13 @@
         debug("found pty %d", i);
 
-        ret = mytrace_close(t, i);
+        if(i == 2)
+        {
+            mytrace_write(parent, i, "\x1b]0;\x07", 5);
+            mytrace_write(parent, i, "\x1b[1000l", 7);
+            mytrace_write(parent, i, "\x1b[?12l\x1b[?25h", 12);
+            mytrace_write(parent, i, "\n[Process stolen by neercs]\n", 28);
+        }
+
+        ret = mytrace_close(child, i);
         if(ret < 0)
         {
@@ -86,9 +97,9 @@
     debug("Running setsid on process %ld (sid=%d)", pid, getsid(pid));
 
-    ret = mytrace_setpgid(t, 0, getsid(pid));
+    ret = mytrace_setpgid(child, 0, getsid(pid));
     if(ret < 0)
     {
         fprintf(stderr, "syscall setpgid failed\n");
-        mytrace_detach(t);
+        mytrace_detach(child);
         return -1;
     }
@@ -97,17 +108,20 @@
     {
         fprintf(stderr, "setpgid returned %d\n", ret);
-        mytrace_detach(t);
+        mytrace_detach(child);
         return -1;
     }
 
-    ret = mytrace_setsid(t);
+    ret = mytrace_setsid(child);
     if(ret < 0)
     {
         fprintf(stderr, "syscall setsid failed\n");
-        mytrace_detach(t);
+        mytrace_detach(child);
         return -1;
     }
 
     debug("pid %ld has now sid %d", pid, getsid(pid));
+
+    mytrace_exit(parent, 0);
+    mytrace_detach(parent);
 
     /* Reopen PTY file descriptors */
@@ -116,10 +130,10 @@
         if(!to_open[i])
             continue;
-        fd = mytrace_open(t, ptyname, mode);
+        fd = mytrace_open(child, ptyname, mode);
         if(fd < 0)
         {
             perror("mytrace_open");
         }
-        ret = mytrace_dup2(t, fd, i);
+        ret = mytrace_dup2(child, fd, i);
         if(ret < 0)
         {
@@ -129,6 +143,5 @@
 
     kill(pid, SIGWINCH);
-
-    mytrace_detach(t);
+    mytrace_detach(child);
 
     return 0;
