Permalink
Browse files

Merge branch 'force-bg'

  • Loading branch information...
2 parents c03bd97 + c1019cf commit 4cbc08a991c0b8332ec14356119a56d437b8ce0e @nelhage committed Jan 28, 2011
Showing with 99 additions and 43 deletions.
  1. +0 −6 BUGS
  2. +0 −12 README
  3. +94 −20 attach.c
  4. +5 −5 ptrace.c
View
6 BUGS
@@ -5,9 +5,3 @@
- Attaching to a process with children doesn't work right.
This should be possible to fix -- I just need to ptrace each child
individually and do the same games to it.
-
-- After attaching to a curses application, the arrow keys may not work
- right. This is because of terminal state that is configured by
- sending control characters to the terminal emulator, instead of
- through termios, and so I can't easily restore. Suspending and
- resuming the app will often usually perform the appropriate reset.
View
12 README
@@ -21,18 +21,6 @@ background it, you will still have to run "bg" or "fg" in the old
terminal. This is likely impossible to fix in a reasonable way without
patching your shell.)
-After attaching, you may need to send a ^L or similar to ncurses
-applications to force them to redraw themselves. With "less", after
-attaching, a ^Z will cause it to both redraw and to set up the
-terminal application keys so that you can scroll with arrow keys
-again.
-
-After attaching, your old terminal will probably be left in a strange
-state, since it will be waiting for the process to quit, but not
-getting any output. You can either background your process and do 'bg;
-disown', or just close the window -- reptyr should protect your
-process from being killed when you do so.
-
"But wait, isn't this just screenify?"
--------------------------------------
View
114 attach.c
@@ -12,6 +12,8 @@
#include <sys/wait.h>
#include <signal.h>
#include <limits.h>
+#include <time.h>
+#include <sys/time.h>
#include "ptrace.h"
#include "reptyr.h"
@@ -145,9 +147,9 @@ int do_setsid(struct ptrace_child *child) {
kill(dummy.pid, SIGKILL);
ptrace_detach_child(&dummy);
ptrace_wait(&dummy);
- ptrace_remote_syscall(child, __NR_waitid,
- P_PID, dummy.pid, 0, WNOHANG,
- 0, 0);
+ ptrace_remote_syscall(child, __NR_wait4,
+ dummy.pid, 0, WNOHANG,
+ 0, 0, 0);
return err;
}
@@ -172,6 +174,84 @@ int ignore_hup(struct ptrace_child *child, unsigned long scratch_page) {
return err;
}
+/*
+ * Wait for the specific pid to enter state 'T', or stopped. We have to pull the
+ * /proc file rather than attaching with ptrace() and doing a wait() because
+ * half the point of this exercise is for the process's real parent (the shell)
+ * to see the TSTP.
+ *
+ * In case the process is masking or ignoring SIGTSTP, we time out after a
+ * second and continue with the attach -- it'll still work mostly right, you
+ * just won't get the old shell back.
+ */
+void wait_for_stop(pid_t pid) {
+ struct timeval start, now;
+ struct timespec sleep;
+ char stat_path[PATH_MAX], buf[256], *p;
+ int fd;
+
+ snprintf(stat_path, sizeof stat_path, "/proc/%d/stat", pid);
+ fd = open(stat_path, O_RDONLY);
+ if (!fd) {
+ error("Unable to open %s: %s", stat_path, strerror(errno));
+ return;
+ }
+ gettimeofday(&start, NULL);
+ while (1) {
+ gettimeofday(&now, NULL);
+ if ((now.tv_sec > start.tv_sec && now.tv_usec > start.tv_usec)
+ || (now.tv_sec - start.tv_sec > 1)) {
+ error("Timed out waiting for child stop.");
+ break;
+ }
+ /*
+ * If anything goes wrong reading or parsing the stat node, just give
+ * up.
+ */
+ lseek(fd, 0, SEEK_SET);
+ if (read(fd, buf, sizeof buf) <= 0)
+ break;
+ p = strchr(buf, ' ');
+ if (!p)
+ break;
+ p = strchr(p+1, ' ');
+ if (!p)
+ break;
+ if (*(p+1) == 'T')
+ break;
+
+ sleep.tv_sec = 0;
+ sleep.tv_nsec = 10000000;
+ nanosleep(&sleep, NULL);
+ }
+ close(fd);
+}
+
+int copy_tty_state(pid_t pid, const char *pty) {
+ char buf[PATH_MAX];
+ int fd, err = 0;
+ struct termios tio;
+
+ snprintf(buf, sizeof buf, "/proc/%d/fd/0", pid);
+ if ((fd = open(buf, O_RDONLY)) < 0)
+ return -errno;
+
+ if (tcgetattr(fd, &tio) < 0) {
+ err = errno;
+ goto out;
+ }
+ close(fd);
+
+ if ((fd = open(pty, O_RDONLY)) < 0)
+ return -errno;
+
+ if (tcsetattr(fd, TCSANOW, &tio) < 0)
+ err = errno;
+out:
+ close(fd);
+ return -err;
+}
+
int attach_child(pid_t pid, const char *pty) {
struct ptrace_child child;
unsigned long scratch_page = -1;
@@ -180,9 +260,16 @@ int attach_child(pid_t pid, const char *pty) {
int err = 0;
long page_size = sysconf(_SC_PAGE_SIZE);
+ if ((err = copy_tty_state(pid, pty)) < 0)
+ return -err;
+
+ kill(pid, SIGTSTP);
+ wait_for_stop(pid);
- if (ptrace_attach_child(&child, pid))
+ if (ptrace_attach_child(&child, pid)) {
+ kill(pid, SIGCONT);
return child.error;
+ }
if (ptrace_advance_to_state(&child, ptrace_at_syscall)) {
err = child.error;
@@ -227,21 +314,6 @@ int attach_child(pid_t pid, const char *pty) {
debug("Opened the new tty in the child: %d", child_fd);
- err = ptrace_remote_syscall(&child, __NR_ioctl,
- child_tty_fds[0], TCGETS, scratch_page,
- 0, 0, 0);
- debug("TCGETS(%d): %d", child_tty_fds[0], err);
- if(err < 0)
- goto out_close;
- err = ptrace_remote_syscall(&child, __NR_ioctl,
- child_fd, TCSETS, scratch_page,
- 0, 0, 0);
- debug("TCSETS: %d", err);
- if (err < 0)
- goto out_close;
-
- debug("Copied terminal settings");
-
err = ignore_hup(&child, scratch_page);
if (err < 0)
goto out_close;
@@ -290,8 +362,10 @@ int attach_child(pid_t pid, const char *pty) {
out_detach:
ptrace_detach_child(&child);
- if (err == 0)
+ if (err == 0) {
+ kill(child.pid, SIGCONT);
kill(child.pid, SIGWINCH);
+ }
return err < 0 ? -err : err;
}
View
@@ -105,10 +105,6 @@ int ptrace_wait(struct ptrace_child *child) {
ptrace_command(child, PTRACE_GETEVENTMSG, 0, &child->forked_pid);
if (child->state != ptrace_at_syscall)
child->state = ptrace_stopped;
- if (sig != SIGSTOP && sig != SIGTRAP && sig != SIGCHLD && sig != SIGHUP && sig != SIGCONT) {
- child->error = EAGAIN;
- return -1;
- }
}
} else {
child->error = EINVAL;
@@ -122,8 +118,12 @@ int ptrace_advance_to_state(struct ptrace_child *child,
int err;
while(child->state != desired) {
switch(desired) {
- case ptrace_at_syscall:
case ptrace_after_syscall:
+ case ptrace_at_syscall:
+ if (WIFSTOPPED(child->status) && WSTOPSIG(child->status) == SIGSEGV) {
+ child->error = EAGAIN;
+ return -1;
+ }
err = ptrace_command(child, PTRACE_SYSCALL, 0, 0);
break;
case ptrace_running:

0 comments on commit 4cbc08a

Please sign in to comment.