Ever since I wrote reptyr, I’ve been frustrated by a number of issues in reptyr that I fundamentally didn’t know how to solve within the reptyr model. Most annoyingly, reptyr fundamentally only worked on single processes, and could not attach processes with children, making it useless in a large class of real-world situations.
TTY stealing 🔗︎
Recently, I merged an experimental reptyr feature that I call “tty-stealing”, which has the potential to fix all of these issues (with some other disadvantages, which I’ll discuss later).
To try it out, clone and build reptyr from git master, and
attach a process using
reptyr -T PID.
Unlike in the default mode,
reptyr -T will attach the entire
terminal session of
PID, not just the single process. For instance,
if you attach a
less process and then hit
q, you’ll find yourself
sitting at the same shell that launched
less, not the shell you
reptyr from. Exit that shell, and you’ll be back at your
One unfortunate known issue:
reptyr -T cannot attach sessions that
were started directly from an
ssh session unless it is run as
root. Read on to learn why.
How it works 🔗︎
In its default mode of operation,
reptyr attaches (via
ptrace(2)) directly to the target process, and actually swaps out
the terminal file descriptors to point at a new terminal. This mode of
operation explains (mostly) why we can’t attach process trees: We
would need to attach to every process individually, and coordinate the
switchover (there are actually even deeper reasons related to the tty
handling, but that’s the basic story).
reptyr -T tackles the problem from the other end. When you’re
running a terminal session on a modern machine, the terminal device
that your shell and commands run on is a “pseudo-terminal”, a virtual
terminal device that acts essentially like a two-way pipe, with a
bunch of extra input-handling behavior. When your
bash writes to
stdout, that data flows over the pty, and is read by the terminal
emulator (which might be
gnome-terminal, or might be
ssh), which then displays it to you in some way.
Instead of switching out terminals in the target process,
goes after the other end of the
pty, the so-called
reptyr -T attempts to discover the pid of the terminal
emulator, attaches to it via
ptrace(2), and finds the fd
corresponding to the master end of the target pty. It then opens a
UNIX socket, and uses
to send the file descriptor back to the
closes the master fd in the original terminal emulator (opening
/dev/null over it, to minimize disruption), and detaches from the
reptyr then takes over the role of terminal emulator, reading from
the the master fd and copying output to its own terminal, and copying
input from its terminal back to the master fd. This has the net effect
that you appear to be connected directly to the target session.
Because this process does not touch the terminal session at all, it is
entirely transparent to the process(es) being attached, which and
works in a large number of circumstances where vanilla
Unfortunately, the new mode isn’t quite flawless.
terminal emulator instead of the target process brings a few new
The most notable one is that you need to be able to attach to the
emulator process at all. In the case of an ssh session, that means the
sshd child for your connection.
sshd drops privileges to
match the authenticated user (via
setuid(2) and friends), so the
emulator process does match the user ID of your user
account. However, Linux forbids users from
ptrace()ing a process
that has undergone a UID transition via
setuid, and so we can’t
attach to that process to steal the master end of the pty.
(The reasoning for this restriction confused me at first, but is
perfectly sound: Even though
sshd has dropped privileges, it might
still contain sensitive information in its heap, such as the machine’s
ssh private keys. We can’t be confident it’s truly unprivileged (and
thus safe to sketch on) until it’s called
execve and cleared out all
Try it out! 🔗︎
I’ve tested out
reptyr -T a fair bit, but it hasn’t gotten broad
testing or usage yet. I’d love your reports of what it does or doesn’t
work on, for you! Try it, and drop me an email or open an issue!