Skip to content

Commit

Permalink
Merge branch 'force-bg'
Browse files Browse the repository at this point in the history
  • Loading branch information
nelhage committed Jan 28, 2011
2 parents c03bd97 + c1019cf commit 4cbc08a
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 43 deletions.
6 changes: 0 additions & 6 deletions BUGS
Expand Up @@ -5,9 +5,3 @@
- Attaching to a process with children doesn't work right. - Attaching to a process with children doesn't work right.
This should be possible to fix -- I just need to ptrace each child This should be possible to fix -- I just need to ptrace each child
individually and do the same games to it. 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.
12 changes: 0 additions & 12 deletions README
Expand Up @@ -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 terminal. This is likely impossible to fix in a reasonable way without
patching your shell.) 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?" "But wait, isn't this just screenify?"
-------------------------------------- --------------------------------------


Expand Down
114 changes: 94 additions & 20 deletions attach.c
Expand Up @@ -12,6 +12,8 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <signal.h> #include <signal.h>
#include <limits.h> #include <limits.h>
#include <time.h>
#include <sys/time.h>


#include "ptrace.h" #include "ptrace.h"
#include "reptyr.h" #include "reptyr.h"
Expand Down Expand Up @@ -145,9 +147,9 @@ int do_setsid(struct ptrace_child *child) {
kill(dummy.pid, SIGKILL); kill(dummy.pid, SIGKILL);
ptrace_detach_child(&dummy); ptrace_detach_child(&dummy);
ptrace_wait(&dummy); ptrace_wait(&dummy);
ptrace_remote_syscall(child, __NR_waitid, ptrace_remote_syscall(child, __NR_wait4,
P_PID, dummy.pid, 0, WNOHANG, dummy.pid, 0, WNOHANG,
0, 0); 0, 0, 0);
return err; return err;
} }


Expand All @@ -172,6 +174,84 @@ int ignore_hup(struct ptrace_child *child, unsigned long scratch_page) {
return err; 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) { int attach_child(pid_t pid, const char *pty) {
struct ptrace_child child; struct ptrace_child child;
unsigned long scratch_page = -1; unsigned long scratch_page = -1;
Expand All @@ -180,9 +260,16 @@ int attach_child(pid_t pid, const char *pty) {
int err = 0; int err = 0;
long page_size = sysconf(_SC_PAGE_SIZE); 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; return child.error;
}


if (ptrace_advance_to_state(&child, ptrace_at_syscall)) { if (ptrace_advance_to_state(&child, ptrace_at_syscall)) {
err = child.error; err = child.error;
Expand Down Expand Up @@ -227,21 +314,6 @@ int attach_child(pid_t pid, const char *pty) {


debug("Opened the new tty in the child: %d", child_fd); 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); err = ignore_hup(&child, scratch_page);
if (err < 0) if (err < 0)
goto out_close; goto out_close;
Expand Down Expand Up @@ -290,8 +362,10 @@ int attach_child(pid_t pid, const char *pty) {
out_detach: out_detach:
ptrace_detach_child(&child); ptrace_detach_child(&child);


if (err == 0) if (err == 0) {
kill(child.pid, SIGCONT);
kill(child.pid, SIGWINCH); kill(child.pid, SIGWINCH);
}


return err < 0 ? -err : err; return err < 0 ? -err : err;
} }
10 changes: 5 additions & 5 deletions ptrace.c
Expand Up @@ -105,10 +105,6 @@ int ptrace_wait(struct ptrace_child *child) {
ptrace_command(child, PTRACE_GETEVENTMSG, 0, &child->forked_pid); ptrace_command(child, PTRACE_GETEVENTMSG, 0, &child->forked_pid);
if (child->state != ptrace_at_syscall) if (child->state != ptrace_at_syscall)
child->state = ptrace_stopped; child->state = ptrace_stopped;
if (sig != SIGSTOP && sig != SIGTRAP && sig != SIGCHLD && sig != SIGHUP && sig != SIGCONT) {
child->error = EAGAIN;
return -1;
}
} }
} else { } else {
child->error = EINVAL; child->error = EINVAL;
Expand All @@ -122,8 +118,12 @@ int ptrace_advance_to_state(struct ptrace_child *child,
int err; int err;
while(child->state != desired) { while(child->state != desired) {
switch(desired) { switch(desired) {
case ptrace_at_syscall:
case ptrace_after_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); err = ptrace_command(child, PTRACE_SYSCALL, 0, 0);
break; break;
case ptrace_running: case ptrace_running:
Expand Down

0 comments on commit 4cbc08a

Please sign in to comment.