Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Be smarter about choosing which fd's to attach.

Instead of using readlink, stat() the child's file descriptors and
compare the result to the device in /proc/$pid/stat and /dev/tty. This
lets us properly attach a 'less' that is taking input from a pipe.
  • Loading branch information...
commit b81b708fe46888ca5c17f5632efd031ba373954b 1 parent 9ce2526
@nelhage authored
Showing with 89 additions and 55 deletions.
  1. +89 −55 attach.c
View
144 attach.c
@@ -35,6 +35,7 @@
#include <limits.h>
#include <time.h>
#include <sys/time.h>
+#include <sys/stat.h>
#include "ptrace.h"
#include "reptyr.h"
@@ -45,29 +46,59 @@
#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;
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)
@@ -75,12 +106,11 @@ int *get_child_tty_fds(struct ptrace_child *child, int *count) {
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;
fds = realloc(fds, sizeof(int) * allocated);
@@ -205,18 +235,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);
@@ -229,51 +252,52 @@ 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;
- goto out;
- }
+ if ((fd = open(buf, O_RDONLY)) < 0) {
+ err = -fd;
+ continue;
+ }
- if (tcgetattr(fd, &tio) < 0) {
- err = errno;
- goto out;
+ 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;
}
@@ -281,25 +305,33 @@ int copy_tty_state(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) {
- if (err == -ENOTTY && !force_stdio) {
+ 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;
+ 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)) {
@@ -333,7 +365,7 @@ int attach_child(pid_t pid, const char *pty, int force_stdio) {
child_tty_fds[1] = 1;
child_tty_fds[2] = 2;
} else {
- child_tty_fds = get_child_tty_fds(&child, &n_fds);
+ child_tty_fds = get_child_tty_fds(&child, statfd, &n_fds);
if (!child_tty_fds) {
err = child.error;
goto out_unmap;
@@ -407,10 +439,12 @@ int attach_child(pid_t pid, const char *pty, int force_stdio) {
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;
}
Please sign in to comment.
Something went wrong with that request. Please try again.