diff --git a/.travis.yml b/.travis.yml
index 84a3ad6..048a2da 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,15 @@
 script: make test
+sudo: false
+language: python
+python:
+  - "2.7"
+install:
+  - env -u CFLAGS -u LDFLAGS pip install -r test/requirements.txt
+addons:
+  apt:
+    packages:
+      - gcc-multilib
+      - libcap-dev
 env:
   - CFLAGS=-m32 LDFLAGS=-m32 DISABLE_TESTS=1
   - CFLAGS=-m64 LDFLAGS=-m64
-before_install:
-  - sudo apt-get update
-  - sudo apt-get -y install gcc-multilib python-pexpect
diff --git a/Makefile b/Makefile
index 0fb5b0d..7127912 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-override CFLAGS+=-Wall -Werror -D_GNU_SOURCE -g
+override CFLAGS := -Wall -Werror -D_GNU_SOURCE -g $(CFLAGS)
 OBJS=reptyr.o reallocarray.o attach.o
 UNAME_S := $(shell uname -s)
 ifeq ($(UNAME_S),Linux)
@@ -8,11 +8,15 @@ ifeq ($(UNAME_S),FreeBSD)
 	OBJS += platform/freebsd/freebsd_ptrace.o platform/freebsd/freebsd.o
 	LDFLAGS += -lprocstat
 endif
-# Note that because of how Make works, this can be overriden from the
+# Note that because of how Make works, this can be overridden from the
 # command-line.
 #
 # e.g. install to /usr with `make PREFIX=/usr`
 PREFIX=/usr/local
+BINDIR=$(PREFIX)/bin
+MANDIR=$(PREFIX)/share/man
+
+PKG_CONFIG ?= pkg-config
 
 all: reptyr
 
@@ -39,12 +43,18 @@ ptrace.o: ptrace.h platform/platform.h $(wildcard platform/*/arch/*.h)
 clean:
 	rm -f reptyr $(OBJS) test/victim.o test/victim
 
+BASHCOMPDIR ?= $(shell $(PKG_CONFIG) --variable=completionsdir bash-completion 2>/dev/null)
+
 install: reptyr
-	install -d -m 755 $(DESTDIR)$(PREFIX)/bin/
-	install -m 755 reptyr $(DESTDIR)$(PREFIX)/bin/reptyr
-	install -d -m 755 $(DESTDIR)$(PREFIX)/share/man/man1
-	install -m 644 reptyr.1 $(DESTDIR)$(PREFIX)/share/man/man1/reptyr.1
-	install -d -m 755 $(DESTDIR)$(PREFIX)/share/man/fr/man1
-	install -m 644 reptyr.fr.1 $(DESTDIR)$(PREFIX)/share/man/fr/man1/reptyr.1
+	install -d -m 755 $(DESTDIR)$(BINDIR)
+	install -m 755 reptyr $(DESTDIR)$(BINDIR)/reptyr
+	install -d -m 755 $(DESTDIR)$(MANDIR)/man1
+	install -m 644 reptyr.1 $(DESTDIR)$(MANDIR)/man1/reptyr.1
+	install -d -m 755 $(DESTDIR)$(MANDIR)/fr/man1
+	install -m 644 reptyr.fr.1 $(DESTDIR)$(MANDIR)/fr/man1/reptyr.1
+	bashcompdir=$(BASHCOMPDIR) ; \
+	test -z "$$bashcompdir" && bashcompdir=/etc/bash_completion.d ; \
+	install -d -m 755 $(DESTDIR)$$bashcompdir ; \
+	install -m 644 reptyr.bash $(DESTDIR)$$bashcompdir/reptyr
 
 .PHONY: PHONY
diff --git a/README.md b/README.md
index cba124c..6c3c6c5 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,21 @@ 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.)
 
+Typical usage pattern
+---------------------
+
+* Start a long running process, e.g. `top`
+* Background the process with CTRL-Z
+* Resume the process in the background: `bg`
+* Display your running background jobs with `jobs -l`, this should look like this:
+  * `[1]+  4711 Stopped (signal)        top`
+  * (The `-l` in `jobs -l` makes sure you'll get the PID)
+* Disown the jobs from the current parent with `disown top`. After that, `jobs` will not show the job any more, but `ps -a` will.
+* Start your terminal multiplexer of choice, e.g. `tmux`
+* Reattach to the backgrounded process: `reptyr 4711`
+* Detach your terminal multiplexer (e.g. CTRL-A D) and close ssh
+* Reconnect ssh, attach to your multiplexer (e.g. `tmux attach`), rejoice!
+
 "But wait, isn't this just screenify?"
 --------------------------------------
 
@@ -41,11 +56,16 @@ accomplishes this.
 PORTABILITY
 -----------
 
-reptyr is Linux-only. It uses ptrace to attach to the target and control it at
-the syscall level, so it is highly dependent on Linux's particular syscall API,
-syscalls, and terminal ioctl()s. A port to Solaris or BSD may be technically
-feasible, but would probably require significant re-architecting to abstract out
-the platform-specific bits.
+reptyr supports Linux and FreeBSD. Not all functionality is currently
+available on FreeBSD. (Notably, FreeBSD doesn't support `reptyr -T` at
+this time.
+
+`reptyr` uses ptrace to attach to the target and control it at the
+syscall level, so it is highly dependent on details of the syscall
+API, available syscalls, and terminal ioctl()s. A port to other
+operating systems may be technically feasible, but requires
+significant low-level knowledge of the relevant platform, and may
+entail significant refactors.
 
 reptyr works on i386, x86_64, and ARM. Ports to other architectures should be
 straightforward, and should in most cases be as simple as adding an arch/ARCH.h
diff --git a/Vagrantfile b/Vagrantfile
index d8696e2..f0319f9 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -23,6 +23,14 @@ EOS
     machine.vm.box = 'chef/fedora-20'
   end
 
+  config.vm.define 'centos-6-x86_64' do |machine|
+    machine.vm.box = 'bento/centos-6.7'
+  end
+
+  config.vm.define 'centos-5-x86_64' do |machine|
+    machine.vm.box = 'bento/centos-5.11'
+  end
+
   config.vm.synced_folder ".", "/vagrant", type: 'nfs', id: 'vagrant-root'
 
 end
diff --git a/attach.c b/attach.c
index 4e65d25..8d9cbf8 100644
--- a/attach.c
+++ b/attach.c
@@ -237,6 +237,16 @@ int grab_pid(pid_t pid, struct ptrace_child *child, child_addr_t *scratch) {
     return err;
 }
 
+int preflight_check(pid_t pid) {
+    struct ptrace_child child;
+    debug("Making sure we have permission to attach...");
+    if (ptrace_attach_child(&child, pid)) {
+        return child.error;
+    }
+    ptrace_detach_child(&child);
+    return 0;
+}
+
 int attach_child(pid_t pid, const char *pty, int force_stdio) {
     struct ptrace_child child;
     child_addr_t scratch_page = -1;
@@ -252,6 +262,10 @@ int attach_child(pid_t pid, const char *pty, int force_stdio) {
         return err;
     }
 
+    if ((err = preflight_check(pid))) {
+        return err;
+    }
+
     debug("Using tty: %s", pty);
 
     if ((err = copy_tty_state(pid, pty))) {
@@ -375,8 +389,11 @@ int setup_steal_socket(struct steal_pty_state *steal) {
         return errno;
 
     steal->addr_un.sun_family = AF_UNIX;
-    snprintf(steal->addr_un.sun_path, sizeof(steal->addr_un.sun_path),
-             "%s/reptyr.sock", steal->tmpdir);
+    if (snprintf(steal->addr_un.sun_path, sizeof(steal->addr_un.sun_path),
+                 "%s/reptyr.sock", steal->tmpdir) >= sizeof(steal->addr_un.sun_path)) {
+        error("tmpdir path too long!");
+        return ENAMETOOLONG;
+    }
 
     if ((steal->sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
         return errno;
@@ -384,9 +401,9 @@ int setup_steal_socket(struct steal_pty_state *steal) {
     if (bind(steal->sockfd, &steal->addr, sizeof(steal->addr_un)) < 0)
         return errno;
 
-    if (chown(steal->addr_un.sun_path, steal->target_stat.uid, steal->target_stat.gid) < 0)
+    if (chown(steal->addr_un.sun_path, steal->emulator_uid, -1) < 0)
         debug("chown %s: %s", steal->addr_un.sun_path, strerror(errno));
-    if (chown(steal->tmpdir, steal->target_stat.uid, steal->target_stat.gid) < 0)
+    if (chown(steal->tmpdir, steal->emulator_uid, -1) < 0)
         debug("chown %s: %s", steal->tmpdir, strerror(errno));
 
     return 0;
@@ -484,7 +501,7 @@ int steal_child_pty(struct steal_pty_state *steal) {
 // it doesn't die.
 int steal_block_hup(struct steal_pty_state *steal) {
     struct ptrace_child leader;
-    child_addr_t scratch;
+    child_addr_t scratch = 0;
     int err = 0;
 
     if ((err = grab_pid(steal->target_stat.sid, &leader, &scratch)))
@@ -532,6 +549,9 @@ int steal_pty(pid_t pid, int *pty) {
     struct steal_pty_state steal = {};
     long page_size = sysconf(_SC_PAGE_SIZE);
 
+    if ((err = preflight_check(pid)))
+        goto out;
+
     if ((err = get_terminal_state(&steal, pid)))
         goto out;
 
diff --git a/platform/freebsd/freebsd.h b/platform/freebsd/freebsd.h
index 6b7d41d..e4dce47 100644
--- a/platform/freebsd/freebsd.h
+++ b/platform/freebsd/freebsd.h
@@ -39,9 +39,9 @@
 #include <stdio.h>
 #include <string.h>
 
-#define do_socketcall(child, name, a0, a1, a2, a3, a4)                  \
+#define do_socketcall(child, scratch, name, a0, a1, a2, a3, a4)         \
     ({                                                                  \
-        int __ret=-1;                                                      \
+        int __ret=-1;                                                   \
         if (ptrace_syscall_numbers((child))->nr_##name) {               \
             __ret = do_syscall((child), name, a0, a1, a2, a3, a4, 0);   \
         }                                                               \
diff --git a/platform/freebsd/freebsd_ptrace.c b/platform/freebsd/freebsd_ptrace.c
index de1c708..9632778 100644
--- a/platform/freebsd/freebsd_ptrace.c
+++ b/platform/freebsd/freebsd_ptrace.c
@@ -47,7 +47,7 @@
 static int __ptrace_command(struct ptrace_child *child, int req,
                             void *, int);
 
-#define ptrace_command(cld, req, ...) _ptrace_command(cld, req, ## __VA_ARGS__, NULL, NULL)
+#define ptrace_command(cld, req, ...) _ptrace_command(cld, req, ## __VA_ARGS__, 0, 0)
 #define _ptrace_command(cld, req, addr, data, ...) __ptrace_command((cld), (req), (void*)(addr), (int)(data))
 
 
diff --git a/platform/linux/arch/amd64.h b/platform/linux/arch/amd64.h
index b161035..63c25e7 100644
--- a/platform/linux/arch/amd64.h
+++ b/platform/linux/arch/amd64.h
@@ -65,7 +65,7 @@ struct syscall_numbers arch_syscall_numbers[2] = {
          * include unistd_32.h, but those definitions would conflict with the
          * standard ones. So, let's just hardcode the values for now. Probably
          * we should generate this from unistd_32.h during the build process or
-         * soemthing.
+         * something.
          */
         .nr_mmap    = 90,
         .nr_mmap2   = 192,
diff --git a/platform/linux/linux.c b/platform/linux/linux.c
index 45a6ff1..448c6a2 100644
--- a/platform/linux/linux.c
+++ b/platform/linux/linux.c
@@ -46,13 +46,6 @@ int parse_proc_stat(int statfd, struct proc_stat *out) {
     }
     out->ctty = dev;
 
-    struct stat st;
-    if (fstat(statfd, &st) != 0)
-        return assert_nonzero(errno);
-
-    out->uid = st.st_uid;
-    out->gid = st.st_gid;
-
     return 0;
 }
 
@@ -67,8 +60,51 @@ int read_proc_stat(pid_t pid, struct proc_stat *out) {
         error("Unable to open %s: %s", stat_path, strerror(errno));
         return -statfd;
     }
-
     err = parse_proc_stat(statfd, out);
+
+
+    close(statfd);
+    return err;
+}
+
+int read_uid(pid_t pid, uid_t *out) {
+    char stat_path[PATH_MAX];
+    char buf[1024];
+    int statfd;
+    int err = 0;
+    int n;
+    char *p = buf;
+
+    snprintf(stat_path, sizeof stat_path, "/proc/%d/status", pid);
+    statfd = open(stat_path, O_RDONLY);
+    if (statfd < 0) {
+        error("Unable to open %s: %s", stat_path, strerror(errno));
+        return -statfd;
+    }
+
+    if ((n = read(statfd, buf, sizeof(buf))) < 0) {
+        err = assert_nonzero(errno);
+        goto out;
+    }
+    while (p < buf + n) {
+        if (strncmp(p, "Uid:\t", strlen("Uid:\t")) == 0)
+            break;
+        p = memchr(p, '\n', buf+n-p);
+        if (p == NULL)
+            break;
+        p++;
+        continue;
+    }
+    if (p == NULL || p >= buf + n) {
+        debug("Unable to parse emulator uid: no Uid line found");
+        *out = -1;
+        goto out;
+    }
+    if(sscanf(p, "Uid:\t%d", out) < 0) {
+        debug("Unable to parse emulator uid: unparseable Uid line");
+    }
+
+ out:
     close(statfd);
     return err;
 }
@@ -148,7 +184,7 @@ int check_pgroup(pid_t target) {
                 memcpy(pid_stat.comm, "???", 4);
             }
             error("Process %d (%.*s) shares %d's process group. Unable to attach.\n"
-                  "(This most commonly means that %d has suprocesses).",
+                  "(This most commonly means that %d has sub-processes).",
                   (int)pid, TASK_COMM_LENGTH, pid_stat.comm, (int)target, (int)target);
             err = EINVAL;
             goto out;
@@ -192,9 +228,10 @@ int *get_child_tty_fds(struct ptrace_child *child, int statfd, int *count) {
     }
 
     if (stat("/dev/console", &console_st) < 0) {
-        child->error = errno;
         error("Unable to stat /dev/console");
-        return NULL;
+        console_st = (struct stat){
+            .st_rdev = -1,
+        };
     }
 
     snprintf(buf, sizeof buf, "/proc/%d/fd/", child->pid);
@@ -237,6 +274,9 @@ int get_terminal_state(struct steal_pty_state *steal, pid_t target) {
     if ((err = find_terminal_emulator(steal)))
         return err;
 
+    if ((err = read_uid(steal->emulator_pid, &steal->emulator_uid)))
+        return err;
+
     return 0;
 }
 
diff --git a/platform/linux/linux.h b/platform/linux/linux.h
index 3ad600f..3ec5a99 100644
--- a/platform/linux/linux.h
+++ b/platform/linux/linux.h
@@ -24,6 +24,14 @@
 #define LINUX_H
 
 #ifdef __linux__
+
+// Important that we include <sys/wait.h> before <linux/*>, for
+// compatibility with certain older libcs, in which <linux/wait.h>
+// #define's some symbols that <sys/wait.h> defines via
+// `enum. c.f. https://bugs.launchpad.net/ubuntu/+source/eglibc/+bug/1261872/
+// and https://github.com/nelhage/reptyr/issues/67
+#include <sys/wait.h>
+
 #include <linux/major.h>
 #include <linux/net.h>
 #include <linux/limits.h>
@@ -32,8 +40,8 @@
 #include <sys/ptrace.h>
 #include <asm/ptrace.h>
 #include <sys/types.h>
+#include <sys/sysmacros.h>
 #include <sys/user.h>
-#include <sys/wait.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
diff --git a/platform/linux/linux_ptrace.c b/platform/linux/linux_ptrace.c
index 5549571..893d4af 100644
--- a/platform/linux/linux_ptrace.c
+++ b/platform/linux/linux_ptrace.c
@@ -47,8 +47,13 @@
 	typeof(y) _min2 = (y);			\
 	_min1 < _min2 ? _min1 : _min2; })
 
+#ifdef PTRACE_TRACEME
+static long __ptrace_command(struct ptrace_child *child, int req,
+                             void *, void*);
+#else
 static long __ptrace_command(struct ptrace_child *child, enum __ptrace_request req,
                              void *, void*);
+#endif
 
 #define ptrace_command(cld, req, ...) _ptrace_command(cld, req, ## __VA_ARGS__, NULL, NULL)
 #define _ptrace_command(cld, req, addr, data, ...) __ptrace_command((cld), (req), (void*)(addr), (void*)(data))
@@ -293,8 +298,13 @@ int ptrace_memcpy_from_child(struct ptrace_child *child, void *dst, child_addr_t
     return 0;
 }
 
+#ifdef PTRACE_TRACEME
+static long __ptrace_command(struct ptrace_child *child, int req,
+                             void *addr, void *data) {
+#else
 static long __ptrace_command(struct ptrace_child *child, enum __ptrace_request req,
                              void *addr, void *data) {
+#endif
     long rv;
     errno = 0;
     rv = ptrace(req, child->pid, addr, data);
diff --git a/platform/platform.h b/platform/platform.h
index 5f10066..ca0fd88 100644
--- a/platform/platform.h
+++ b/platform/platform.h
@@ -23,6 +23,10 @@
 #ifndef PLATFORM_H
 #define PLATFORM_H
 
+#ifdef __APPLE__
+#error "reptyr does not currently support macOS"
+#endif
+
 #include "linux/linux.h"
 #include "freebsd/freebsd.h"
 #include "../ptrace.h"
@@ -45,14 +49,14 @@ struct proc_stat {
     char state;
     pid_t ppid, sid, pgid;
     dev_t ctty;
-    uid_t uid;
-    gid_t gid;
 };
 
 struct steal_pty_state {
     struct proc_stat target_stat;
 
     pid_t emulator_pid;
+    uid_t emulator_uid;
+
     struct fd_array master_fds;
 
     char tmpdir[PATH_MAX];
diff --git a/reptyr.bash b/reptyr.bash
new file mode 100644
index 0000000..2eb8d9b
--- /dev/null
+++ b/reptyr.bash
@@ -0,0 +1,19 @@
+# bash completion for reptyr(1)
+
+_reptyr()
+{
+    case $3 in
+        -l|-L|-h|-v) return ;;
+    esac
+
+    if [[ $2 == -* ]]; then
+        COMPREPLY=( $(compgen -W '-l -L -s -T -h -v -V' -- "$2") )
+        return
+    fi
+
+    case $3 in
+        [1-9]*) ;;
+        *) COMPREPLY=( $(compgen -W '$(command ps axo pid=)' -- "$2") ) ;;
+    esac
+} &&
+complete -F _reptyr reptyr
diff --git a/reptyr.c b/reptyr.c
index 4563af6..1b8f587 100644
--- a/reptyr.c
+++ b/reptyr.c
@@ -124,23 +124,34 @@ void do_proxy(int pty) {
     char buf[4096];
     ssize_t count;
     fd_set set;
-    struct timeval timeout;
+    sigset_t mask;
+    sigset_t select_mask;
+    struct sigaction sa;
+
+    // Block WINCH while we're outside the select, but unblock it
+    // while we're inside:
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGWINCH);
+    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
+        fprintf(stderr, "sigprocmask: %m");
+        return;
+    }
+    sa.sa_handler = do_winch;
+    sa.sa_flags = 0;
+    sigemptyset(&sa.sa_mask);
+    sigaction(SIGWINCH, &sa, NULL);
+    resize_pty(pty);
+
     while (1) {
         if (winch_happened) {
             winch_happened = 0;
-            /*
-             * FIXME: If a signal comes in after this point but before
-             * select(), the resize will be delayed until we get more
-             * input. signalfd() is probably the cleanest solution.
-             */
             resize_pty(pty);
         }
         FD_ZERO(&set);
         FD_SET(0, &set);
         FD_SET(pty, &set);
-        timeout.tv_sec = 0;
-        timeout.tv_usec = 1000;
-        if (select(pty + 1, &set, NULL, NULL, &timeout) < 0) {
+        sigemptyset(&select_mask);
+        if (pselect(pty + 1, &set, NULL, NULL, NULL, &select_mask) < 0) {
             if (errno == EINTR)
                 continue;
             fprintf(stderr, "select: %m");
@@ -179,7 +190,6 @@ void usage(char *me) {
 
 int main(int argc, char **argv) {
     struct termios saved_termios;
-    struct sigaction act;
     int pty;
     int opt;
     int err;
@@ -287,11 +297,6 @@ int main(int argc, char **argv) {
     }
 
     setup_raw(&saved_termios);
-    memset(&act, 0, sizeof act);
-    act.sa_handler = do_winch;
-    act.sa_flags   = 0;
-    sigaction(SIGWINCH, &act, NULL);
-    resize_pty(pty);
     do_proxy(pty);
     do {
         errno = 0;
diff --git a/reptyr.fr.1 b/reptyr.fr.1
index 3bf82d5..2007f49 100644
--- a/reptyr.fr.1
+++ b/reptyr.fr.1
@@ -51,7 +51,7 @@ même si vous fermez le shell sans le faire.
 .B \-l
 .IP
 Plutôt que d'attacher un nouveau processus, crée un couveau couple de
-pty, redirige l'extrémité maîtresse vers le terminal en corus, puis
+pty, redirige l'extrémité maîtresse vers le terminal en cours, puis
 affiche le nom du pty esclave. Il pourra être passé en argument par
 exemple à l'option
 .I set inferior-tty
diff --git a/test/requirements.txt b/test/requirements.txt
new file mode 100644
index 0000000..f5cca46
--- /dev/null
+++ b/test/requirements.txt
@@ -0,0 +1,2 @@
+pexpect
+python-prctl>1.6
diff --git a/test/tty-steal.py b/test/tty-steal.py
index 3e1c510..c3e1f8b 100644
--- a/test/tty-steal.py
+++ b/test/tty-steal.py
@@ -3,9 +3,17 @@
 import sys
 
 if os.getenv("NO_TEST_STEAL") is not None:
-    print "Skipping tty-stealing tests because $NO_TEST_STEAL is set."
+    print("Skipping tty-stealing tests because $NO_TEST_STEAL is set.")
     sys.exit(0)
 
+try:
+    import prctl
+    PR_SET_PTRACER_ANY = 0xffffffff
+    if hasattr(prctl, 'set_ptracer'):
+        prctl.set_ptracer(PR_SET_PTRACER_ANY)
+except ImportError:
+    print("Unable to import `prctl`, skipping `PR_SET_PTRACER`.")
+
 child = pexpect.spawn("test/victim")
 child.setecho(False)
 child.sendline("hello")
diff --git a/test/victim.c b/test/victim.c
index cb1bf93..60b5ce8 100644
--- a/test/victim.c
+++ b/test/victim.c
@@ -1,9 +1,14 @@
 #include <stdio.h>
+#include <sys/prctl.h>
 
 int main(int argc, char **argv) {
     char *line = NULL;
     size_t cap = 0;
 
+#ifdef PR_SET_PTRACER
+    prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
+#endif
+
     while(getline(&line, &cap, stdin) != -1) {
         printf("ECHO: %s", line);
     }
