Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'force-bg'

  • Loading branch information...
commit 4cbc08a991c0b8332ec14356119a56d437b8ce0e 2 parents c03bd97 + c1019cf
Nelson Elhage authored January 27, 2011
6  BUGS
@@ -5,9 +5,3 @@
5 5
 - Attaching to a process with children doesn't work right.
6 6
   This should be possible to fix -- I just need to ptrace each child
7 7
   individually and do the same games to it.
8  
-
9  
-- After attaching to a curses application, the arrow keys may not work
10  
-  right. This is because of terminal state that is configured by
11  
-  sending control characters to the terminal emulator, instead of
12  
-  through termios, and so I can't easily restore. Suspending and
13  
-  resuming the app will often usually perform the appropriate reset.
12  README
@@ -21,18 +21,6 @@ background it, you will still have to run "bg" or "fg" in the old
21 21
 terminal. This is likely impossible to fix in a reasonable way without
22 22
 patching your shell.)
23 23
 
24  
-After attaching, you may need to send a ^L or similar to ncurses
25  
-applications to force them to redraw themselves. With "less", after
26  
-attaching, a ^Z will cause it to both redraw and to set up the
27  
-terminal application keys so that you can scroll with arrow keys
28  
-again.
29  
-
30  
-After attaching, your old terminal will probably be left in a strange
31  
-state, since it will be waiting for the process to quit, but not
32  
-getting any output. You can either background your process and do 'bg;
33  
-disown', or just close the window -- reptyr should protect your
34  
-process from being killed when you do so.
35  
-
36 24
 "But wait, isn't this just screenify?"
37 25
 --------------------------------------
38 26
 
114  attach.c
@@ -12,6 +12,8 @@
12 12
 #include <sys/wait.h>
13 13
 #include <signal.h>
14 14
 #include <limits.h>
  15
+#include <time.h>
  16
+#include <sys/time.h>
15 17
 
16 18
 #include "ptrace.h"
17 19
 #include "reptyr.h"
@@ -145,9 +147,9 @@ int do_setsid(struct ptrace_child *child) {
145 147
     kill(dummy.pid, SIGKILL);
146 148
     ptrace_detach_child(&dummy);
147 149
     ptrace_wait(&dummy);
148  
-    ptrace_remote_syscall(child, __NR_waitid,
149  
-                          P_PID, dummy.pid, 0, WNOHANG,
150  
-                          0, 0);
  150
+    ptrace_remote_syscall(child, __NR_wait4,
  151
+                          dummy.pid, 0, WNOHANG,
  152
+                          0, 0, 0);
151 153
     return err;
152 154
 }
153 155
 
@@ -172,6 +174,84 @@ int ignore_hup(struct ptrace_child *child, unsigned long scratch_page) {
172 174
     return err;
173 175
 }
174 176
 
  177
+/*
  178
+ * Wait for the specific pid to enter state 'T', or stopped. We have to pull the
  179
+ * /proc file rather than attaching with ptrace() and doing a wait() because
  180
+ * half the point of this exercise is for the process's real parent (the shell)
  181
+ * to see the TSTP.
  182
+ *
  183
+ * In case the process is masking or ignoring SIGTSTP, we time out after a
  184
+ * second and continue with the attach -- it'll still work mostly right, you
  185
+ * just won't get the old shell back.
  186
+ */
  187
+void wait_for_stop(pid_t pid) {
  188
+    struct timeval start, now;
  189
+    struct timespec sleep;
  190
+    char stat_path[PATH_MAX], buf[256], *p;
  191
+    int fd;
  192
+    
  193
+    snprintf(stat_path, sizeof stat_path, "/proc/%d/stat", pid);
  194
+    fd = open(stat_path, O_RDONLY);
  195
+    if (!fd) {
  196
+        error("Unable to open %s: %s", stat_path, strerror(errno));
  197
+        return;
  198
+    }
  199
+    gettimeofday(&start, NULL);
  200
+    while (1) {
  201
+        gettimeofday(&now, NULL);
  202
+        if ((now.tv_sec > start.tv_sec && now.tv_usec > start.tv_usec)
  203
+            || (now.tv_sec - start.tv_sec > 1)) {
  204
+            error("Timed out waiting for child stop.");
  205
+            break;
  206
+        }
  207
+        /*
  208
+         * If anything goes wrong reading or parsing the stat node, just give
  209
+         * up.
  210
+         */
  211
+        lseek(fd, 0, SEEK_SET);
  212
+        if (read(fd, buf, sizeof buf) <= 0)
  213
+            break;
  214
+        p = strchr(buf, ' ');
  215
+        if (!p)
  216
+            break;
  217
+        p = strchr(p+1, ' ');
  218
+        if (!p)
  219
+            break;
  220
+        if (*(p+1) == 'T')
  221
+            break;
  222
+
  223
+        sleep.tv_sec  = 0;
  224
+        sleep.tv_nsec = 10000000;
  225
+        nanosleep(&sleep, NULL);
  226
+    }
  227
+    close(fd);
  228
+}
  229
+
  230
+int copy_tty_state(pid_t pid, const char *pty) {
  231
+    char buf[PATH_MAX];
  232
+    int fd, err = 0;
  233
+    struct termios tio;
  234
+
  235
+    snprintf(buf, sizeof buf, "/proc/%d/fd/0", pid);
  236
+    if ((fd = open(buf, O_RDONLY)) < 0)
  237
+        return -errno;
  238
+
  239
+    if (tcgetattr(fd, &tio) < 0) {
  240
+        err = errno;
  241
+        goto out;
  242
+    }
  243
+    close(fd);
  244
+
  245
+    if ((fd = open(pty, O_RDONLY)) < 0)
  246
+        return -errno;
  247
+
  248
+    if (tcsetattr(fd, TCSANOW, &tio) < 0)
  249
+        err = errno;
  250
+out:
  251
+    close(fd);
  252
+    return -err;
  253
+}
  254
+
175 255
 int attach_child(pid_t pid, const char *pty) {
176 256
     struct ptrace_child child;
177 257
     unsigned long scratch_page = -1;
@@ -180,9 +260,16 @@ int attach_child(pid_t pid, const char *pty) {
180 260
     int err = 0;
181 261
     long page_size = sysconf(_SC_PAGE_SIZE);
182 262
 
  263
+    if ((err = copy_tty_state(pid, pty)) < 0)
  264
+        return -err;
  265
+
  266
+    kill(pid, SIGTSTP);
  267
+    wait_for_stop(pid);
183 268
 
184  
-    if (ptrace_attach_child(&child, pid))
  269
+    if (ptrace_attach_child(&child, pid)) {
  270
+        kill(pid, SIGCONT);
185 271
         return child.error;
  272
+    }
186 273
 
187 274
     if (ptrace_advance_to_state(&child, ptrace_at_syscall)) {
188 275
         err = child.error;
@@ -227,21 +314,6 @@ int attach_child(pid_t pid, const char *pty) {
227 314
 
228 315
     debug("Opened the new tty in the child: %d", child_fd);
229 316
 
230  
-    err = ptrace_remote_syscall(&child, __NR_ioctl,
231  
-                                child_tty_fds[0], TCGETS, scratch_page,
232  
-                                0, 0, 0);
233  
-    debug("TCGETS(%d): %d", child_tty_fds[0], err);
234  
-    if(err < 0)
235  
-        goto out_close;
236  
-    err = ptrace_remote_syscall(&child, __NR_ioctl,
237  
-                                child_fd, TCSETS, scratch_page,
238  
-                                0, 0, 0);
239  
-    debug("TCSETS: %d", err);
240  
-    if (err < 0)
241  
-        goto out_close;
242  
-
243  
-    debug("Copied terminal settings");
244  
-
245 317
     err = ignore_hup(&child, scratch_page);
246 318
     if (err < 0)
247 319
         goto out_close;
@@ -290,8 +362,10 @@ int attach_child(pid_t pid, const char *pty) {
290 362
  out_detach:
291 363
     ptrace_detach_child(&child);
292 364
 
293  
-    if (err == 0)
  365
+    if (err == 0) {
  366
+        kill(child.pid, SIGCONT);
294 367
         kill(child.pid, SIGWINCH);
  368
+    }
295 369
 
296 370
     return err < 0 ? -err : err;
297 371
 }
10  ptrace.c
@@ -105,10 +105,6 @@ int ptrace_wait(struct ptrace_child *child) {
105 105
                 ptrace_command(child, PTRACE_GETEVENTMSG, 0, &child->forked_pid);
106 106
             if (child->state != ptrace_at_syscall)
107 107
                 child->state = ptrace_stopped;
108  
-            if (sig != SIGSTOP && sig != SIGTRAP && sig != SIGCHLD && sig != SIGHUP && sig != SIGCONT) {
109  
-                child->error = EAGAIN;
110  
-                return -1;
111  
-            }
112 108
         }
113 109
     } else {
114 110
         child->error = EINVAL;
@@ -122,8 +118,12 @@ int ptrace_advance_to_state(struct ptrace_child *child,
122 118
     int err;
123 119
     while(child->state != desired) {
124 120
         switch(desired) {
125  
-        case ptrace_at_syscall:
126 121
         case ptrace_after_syscall:
  122
+        case ptrace_at_syscall:
  123
+            if (WIFSTOPPED(child->status) && WSTOPSIG(child->status) == SIGSEGV) {
  124
+                child->error = EAGAIN;
  125
+                return -1;
  126
+            }
127 127
             err = ptrace_command(child, PTRACE_SYSCALL, 0, 0);
128 128
             break;
129 129
         case ptrace_running:

0 notes on commit 4cbc08a

Please sign in to comment.
Something went wrong with that request. Please try again.