Skip to content

Commit

Permalink
Merge branch 'attach-fds'
Browse files Browse the repository at this point in the history
  • Loading branch information
nelhage committed Mar 2, 2011
2 parents e732f13 + b81b708 commit 64be639
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 66 deletions.
170 changes: 110 additions & 60 deletions attach.c
Expand Up @@ -35,6 +35,7 @@
#include <limits.h>
#include <time.h>
#include <sys/time.h>
#include <sys/stat.h>

#include "ptrace.h"
#include "reptyr.h"
Expand All @@ -45,43 +46,72 @@
#define mmap_syscall __NR_mmap
#endif

#define TASK_COMM_LENGTH 16
struct proc_stat {
pid_t pid;
char comm[TASK_COMM_LENGTH+1];
char state;
pid_t ppid, sid, pgid;
dev_t ctty;
};

int parse_proc_stat(int statfd, struct proc_stat *out) {
char buf[1024];
int n;
lseek(statfd, 0, SEEK_SET);
if (read(statfd, buf, sizeof buf) < 0)
return errno;
n = sscanf(buf, "%d (%16[^)]) %c %d %d %d %u",
&out->pid, out->comm,
&out->state, &out->ppid, &out->sid,
&out->pgid, (unsigned*)&out->ctty);
if (n == EOF)
return errno;
if (n != 7) {
return EINVAL;
}
return 0;
}

static void do_unmap(struct ptrace_child *child, child_addr_t addr, unsigned long len) {
if (addr == (unsigned long)-1)
return;
ptrace_remote_syscall(child, __NR_munmap, addr, len, 0, 0, 0, 0);
}

int *get_child_tty_fds(struct ptrace_child *child, int *count) {
int *get_child_tty_fds(struct ptrace_child *child, int statfd, int *count) {
struct proc_stat child_status;
struct stat tty_st, st;
char buf[PATH_MAX];
char child_tty[PATH_MAX];
int n = 0, allocated = 0;
int *fds = NULL;
DIR *dir;
ssize_t len;
struct dirent *d;
int *tmp = NULL;

debug("Looking up fds for tty in child.");
snprintf(buf, sizeof buf, "/proc/%d/fd/0", child->pid);
len = readlink(buf, child_tty, PATH_MAX);
if(len < 0)
if ((child->error = parse_proc_stat(statfd, &child_status)))
return NULL;

child_tty[len] = 0;
debug("Resolved child tty: %s", child_tty);
debug("Resolved child tty: %u", child_status.ctty);

if (stat("/dev/tty", &tty_st) < 0) {
child->error = errno;
error("Unable to stat /dev/tty");
return NULL;
}

snprintf(buf, sizeof buf, "/proc/%d/fd/", child->pid);
if ((dir = opendir(buf)) == NULL)
return NULL;
while ((d = readdir(dir)) != NULL) {
if (d->d_name[0] == '.') continue;
snprintf(buf, sizeof buf, "/proc/%d/fd/%s", child->pid, d->d_name);
len = readlink(buf, buf, PATH_MAX);
if (len < 0)
if(stat(buf, &st) < 0)
continue;
buf[len] = 0;
if (strcmp(buf, child_tty) == 0
|| strcmp(buf, "/dev/tty") == 0) {

if (st.st_rdev == child_status.ctty
|| st.st_rdev == tty_st.st_rdev) {
if (n == allocated) {
allocated = allocated ? 2 * allocated : 2;
tmp = realloc(fds, allocated * sizeof *tmp);
Expand Down Expand Up @@ -212,18 +242,11 @@ int ignore_hup(struct ptrace_child *child, unsigned long scratch_page) {
* 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) {
void wait_for_stop(pid_t pid, int fd) {
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;
}
struct proc_stat st;

gettimeofday(&start, NULL);
while (1) {
gettimeofday(&now, NULL);
Expand All @@ -236,73 +259,86 @@ void wait_for_stop(pid_t pid) {
* 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)
if (parse_proc_stat(fd, &st))
break;
p = strchr(p+1, ' ');
if (!p)
break;
if (*(p+1) == 'T')
if (st.state == '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;
int i;

snprintf(buf, sizeof buf, "/proc/%d/fd/0", pid);
if ((fd = open(buf, O_RDONLY)) < 0)
return -errno;
for (i = 0; i < 3; i++) {
err = 0;
snprintf(buf, sizeof buf, "/proc/%d/fd/%d", pid, i);

if (!isatty(fd)) {
err = ENOTTY;
error("Target is not connected to a terminal.");
goto out;
}

if (tcgetattr(fd, &tio) < 0) {
err = errno;
goto out;
if ((fd = open(buf, O_RDONLY)) < 0) {
err = -fd;
continue;
}

if (!isatty(fd)) {
err = ENOTTY;
goto retry;
}

if (tcgetattr(fd, &tio) < 0) {
err = -errno;
}
retry:
close(fd);
}
close(fd);

if (err)
return err;

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, int force_stdio) {
struct ptrace_child child;
unsigned long scratch_page = -1;
int *child_tty_fds = NULL, n_fds, child_fd;
int *child_tty_fds = NULL, n_fds, child_fd, statfd;
int i;
int err = 0;
long page_size = sysconf(_SC_PAGE_SIZE);
char stat_path[PATH_MAX];

if ((err = copy_tty_state(pid, pty)) < 0)
return -err;
if ((err = copy_tty_state(pid, pty))) {
if (err == ENOTTY && !force_stdio) {
error("Target is not connected to a terminal.\n"
" Use -s to force attaching anyways.");
return err;
}
}

snprintf(stat_path, sizeof stat_path, "/proc/%d/stat", pid);
statfd = open(stat_path, O_RDONLY);
if (statfd < 0) {
error("Unable to open %s: %s", stat_path, strerror(errno));
return -statfd;
}

kill(pid, SIGTSTP);
wait_for_stop(pid);
wait_for_stop(pid, statfd);

if (ptrace_attach_child(&child, pid)) {
kill(pid, SIGCONT);
return child.error;
err = child.error;
goto out_cont;
}

if (ptrace_advance_to_state(&child, ptrace_at_syscall)) {
Expand All @@ -325,10 +361,22 @@ int attach_child(pid_t pid, const char *pty) {

debug("Allocated scratch page: %lx", scratch_page);

child_tty_fds = get_child_tty_fds(&child, &n_fds);
if (!child_tty_fds) {
err = child.error;
goto out_unmap;
if (force_stdio) {
child_tty_fds = malloc(3 * sizeof(int));
if (!child_tty_fds) {
err = ENOMEM;
goto out_unmap;
}
n_fds = 3;
child_tty_fds[0] = 0;
child_tty_fds[1] = 1;
child_tty_fds[2] = 2;
} else {
child_tty_fds = get_child_tty_fds(&child, statfd, &n_fds);
if (!child_tty_fds) {
err = child.error;
goto out_unmap;
}
}

if (ptrace_memcpy_to_child(&child, scratch_page, pty, strlen(pty)+1)) {
Expand Down Expand Up @@ -398,10 +446,12 @@ int attach_child(pid_t pid, const char *pty) {

if (err == 0) {
kill(child.pid, SIGSTOP);
wait_for_stop(child.pid);
wait_for_stop(child.pid, statfd);
}
kill(child.pid, SIGCONT);
kill(child.pid, SIGWINCH);
out_cont:
kill(child.pid, SIGCONT);
close(statfd);

return err < 0 ? -err : err;
}
19 changes: 14 additions & 5 deletions reptyr.c
Expand Up @@ -139,7 +139,10 @@ void do_proxy(int pty) {
}

void usage(char *me) {
fprintf(stderr, "Usage: %s [-l | PID]\n", me);
fprintf(stderr, "Usage: %s [-s] PID\n", me);
fprintf(stderr, " %s -l\n", me);
fprintf(stderr, " -l Create a new pty pair and print the name of the slave.\n");
fprintf(stderr, " -s Attach fds 0-2 on the target, even if it is not attached to a tty.\n");
}

void check_yama_ptrace_scope(void) {
Expand All @@ -165,20 +168,26 @@ int main(int argc, char **argv) {
struct termios saved_termios;
struct sigaction act;
int pty;
int arg = 1;
int do_attach = 1;
int force_stdio = 0;

if (argc < 2) {
usage(argv[0]);
return 2;
}
if(argv[1][0] == '-') {
switch(argv[1][1]) {
if(argv[arg][0] == '-') {
switch(argv[arg][1]) {
case 'h':
usage(argv[0]);
return 0;
case 'l':
do_attach = 0;
break;
case 's':
arg++;
force_stdio = 1;
break;
case 'v':
printf("This is reptyr version %s.\n", REPTYR_VERSION);
printf(" by Nelson Elhage <nelhage@nelhage.com>\n");
Expand All @@ -198,9 +207,9 @@ int main(int argc, char **argv) {
die("Unable to unlockpt: %m");

if (do_attach) {
pid_t child = atoi(argv[1]);
pid_t child = atoi(argv[arg]);
int err;
if ((err = attach_child(child, ptsname(pty)))) {
if ((err = attach_child(child, ptsname(pty), force_stdio))) {
fprintf(stderr, "Unable to attach to pid %d: %s\n", child, strerror(err));
if (err == EPERM) {
check_yama_ptrace_scope();
Expand Down
2 changes: 1 addition & 1 deletion reptyr.h
Expand Up @@ -22,7 +22,7 @@

#define REPTYR_VERSION "0.1"

int attach_child(pid_t pid, const char *pty);
int attach_child(pid_t pid, const char *pty, int force_stdio);
void die(const char *msg, ...);
void debug(const char *msg, ...);
void error(const char *msg, ...);

0 comments on commit 64be639

Please sign in to comment.