reptyr: Attach a running process to a new terminal
Over the last week, I’ve written a nifty tool that I call reptyr. reptyr is a utility for taking an existing running program and attaching it to a new terminal. Started a long-running process over ssh, but have to leave and don’t want to interrupt it? Just start a screen, use reptyr to grab it, and then kill the ssh session and head on home.
You can grab the source, or read on for some more details.
There’s a shell script called screenify that’s been going
around the internet for nigh on 10 years now that is supposed to use
gdb to accomplish the same thing. There’s also a project called
retty that tries to do the same thing, in C using ptrace()
directly.
The difference between those programs and reptyr is that reptyr works much, much, better.
If you attach a less using screenify or retty, it will still take
input from the old terminal. If you attach an ncurses program, and
resize the window, the program probably won’t resize correctly. ^C
and ^Z will still be processed on the old terminal — typing them in
the new terminal won’t do anything useful.
reptyr fixes all of these problems and more, and is the only such tool I know of that does so. I’ve never seen a program that doesn’t behave noticeably incorrectly after attaching with retty or screenify, whereas with reptyr most programs I have tried work flawlessly.
How does it work?
reptyr works in the same basic way as screenify and retty — it
attaches to the target process using the ptrace API, opens the new
terminal, and dup2s it over the old file descriptors. It also copies
the termios settings from the old terminal to the new terminal.
The main thing that reptyr does that no one else does is that it
actually changes the controlling terminal of the process you are
attaching. This is the detail that makes many things Just Work,
including ^C and ^Z and window resizing.
Switching the target’s controlling terminal is not easy and involves a
fair bit of trickery with ptrace and Linux’s terminal APIs. I will
probably do another blog post some time about the dirty details of how
I make this work, but for now you can check out
attach.c if
you really want to know.
reptyr still has a number of limitations — it doesn’t generally work, for example, if the target process has any children. I know how to fix most of these problems, though, so expect it to get better with time. Please let me know if you find it useful!
Appendix
(Edited to add:) Nothing is really new. A commenter on reddit pointed out that injcode
and neercs both accomplish the same thing, even using the same trick
to change the CTTY. Ah well, I had run writing it anyways, and apparently I
wasn’t the only one who didn’t know about the existing alternatives. neercs is a full screen replacement, though, and I think that reptyr should be more robust than injcode — I use a different techique for ptrace-hijacking, for example — and so hopefully this tool still has a niche as a more robust standalone utility. Certainly, judging from the amount of enthusiasm I’ve seen for this tool, this still isn’t a problem that is solved to the average user’s satisfaction.
I can’t imagine how anyone regularly working with linux machines could find this anything other than an extremely welcome addition to the toolbox. Thanks!
Love it!
Incredible analysis of the existing tools out there. Good job man. Keep it up.
Sounds fantastic, but it does not work for me:
This is on Ubuntu Maverick. Trying to attach a vim session.
@ward Are you able to run it under
straceand send me the output? That would be the easiest way for me to try to figure out what’s going wrong.Holy god man, nice work. When I connected to a vim process the up/down arrows didn’t seem to function properly, and it would be nice if it cleanly returned the other shell.
Also starting in 10.10 ubuntu has ptrace disabled except for child processes and certain exceptions negotiated with kees :-) To disable it, you have to
echo 0 > /proc/sys/kernel/yama/ptrace_scope
But I wouldn’t recommend it for long term, it’s like that for some very good reasons.
There are at least two programs that do this already.
injcode:
http://blog.habets.pp.se/2009/03/Moving-a-process-to-another-terminal
And neercs:
http://caca.zoy.org/wiki/neercs
Very nice, a fairly old hack but nice to have something without the usual problems.
You may be interested to know there is a program called neercs which also does something similar although I haven’t tried it and don’t know what problems it has.
I wish someone would come up with a clean way to do this and I’d be all over it for tmux. I think it’d probably need kernel support though to be worth it; a kernel API would make it easier to port as well.
Works here on x86 Ubuntu 10.4 LTS; tested flawlessly with emacs and watch.
If I didn’t have a permanent screen up on every box I use, this would be more useful. But the moment I connect to a box for the first time, I’m running screen or screen -x…
If I didn’t do that, this would be awesome. :-)
Exactly. This was answered literally decades ago by screen.
@Greyed
no, this is for when you have a process running that wasn’t started in screen, I think.
Haven’t you ever started a process and thought “shiiiiit, I really should have run that in a screen session…”
Love it!
Yes, I do find it really useful, thanks!
The arrow keys is a known problem — the issue is that the arrow keys are configured by state that is kept by the terminal emulator, not by the in-kernel terminal system, and so doesn’t get copied by repytr. If you background vim (via
^Zin the new terminal), and then continue it (via eitherbgin the old terminal, orkill -CONT VIM-PID), vim should reinitialize the terminal and restore the state correctly.Alternately, it’s vim — what do you need arrow keys for? ;)
Doesn’t reptyr do the same thing as this? Which, in turn, did something that this is supposed to do as well. I guess someone will find a use for all these wheels…
@Ceren @Greyed @nelhage
Nelhage: You have effectively succeeded in not explaining what it is you are doing. You mention screen, you write ten not inconsequential paragraphs, make a stab at a usage scenario, but absolutely fail to give a non-relative explanation of what you are doing.
You say “like X, akin to Y”.
I use screen, I know that ^s annoys me, as does ^a being taken over. You say start a screen, and use reptyr – use it for what?
So, It starts in a screen, and reptyr is for attaching to that process onto a normal terminal (and not using screen again to connect to it?)
If that is what it does THEN SAY THAT. Why it is that you haven’t said it? I’ve pastebin’d your original comment for reference, because it just irks me that people are unable to grasps, understand, and coordinate language in a clear-minded way.
Now this is fun code, and when you need this tool you really need it. I hope to find a mature version of this tool on every linux box in a couple years. Great work.
Many thanks for making this tool.
However I failed to run it on my ARM device with the error message:
“Target is not connected to a terminal.”
I checked the /proc and found the stdout was /dev/null.
So it means the tool does not handle this case?
@danath The tool was originally written to attach a process that was already connected to a terminal, to a different terminal. I’ve just pushed a new branch to github —
attach-fds— that supports a-soption that causes it to attach and grab fds 0-2 even if the target is not already attached to a terminal. Let me know if that makes it work in your case.@nelhage It works well! Many thanks to you.
Looking forward to having this powerful tool in the standard coreutils or busybox release.
BTW, my machine is Linux 2.6.27 running on ARM 926.
A Naive comment here, as I don’t know all that much about the structures involved:
Maybe what we want isn’t so much a way to move a process to a new tty, as to move an existing tty under the control of screen?
I understand that solving that problem would be quite a different proposition, but if it’s doable it might be more general and more robust?
Yeah, people worked on something similar, but who would deny you your fun. I hope you continue to work on it and provide updates. It IS extremely useful when you need it.
Oh, and Jack, you just don’t have a clue. This article was well written and can be perfectly understood. The title of the page pretty much tells you everything you need to know. The only thing lacking here is your comprehension ability and your humility
Hey.
Great work. :-)
“I know how to fix most of these problems” … is this already done? I’d love to see all that in code ;)
Cheers, Chris.
Can it be possible that reptyr doesn’t work on PPC-based linux? My Gentoo64 PPC says that this architecture is not supported. (when trying to compile)
@Christoph Anton Mitterer I’ve fixed a number of issues since this post went up, including releasing the old terminal so it can be used again, and implementing support for attaching compat-mode 32-bit processes on amd64. I haven’t fixed attaching things with children; Maybe I’ll attempt to tackle it one of these days.
@KrusjMe Correct, reptyr currently only supports x86 and ARM Linux machines, as noted in the README. Adding support for PPC probably wouldn’t be too difficult, but I don’t have the time or the inclination to do it at the moment, although I would probably accept patches.
@nelhage: Thank you for your quick response. Ah, the README! :) I tried to install it on Gentoo PPC64 using Portage. (the default package-manager of Gentoo) Portage just said something about the architecture. Thanks again!
same problem than @ward here
this is debian sid. Also I have no /proc/sys/kernel/yama folder nor /etc/sysctl.d/10-ptrace.conf file so I can’t go on
echo 0 > /proc/sys/kernel/yama/ptrace_scopeIf applicable, I can email you straced output for the call
the tool however sounds to be a nice hack.
@mzeta Hm, I think that is actually a slightly different error from ward’s. If you could email me the strace output, that would be helpful.
Works great! (Debian Squeeze)
You are a fricking GOD!!!
I needed this realllllllllll bad today. :)
“I had run writing it anyways” typolice: s/had run/had fun/
Adding ppc and mips support is now on my to do list. It’s a long list so don’t get your hopes up. :-)
This is incredible stuff ! Thank you Mister nelhage
I have the same problem as mzeta. Did he send you the strace output and did you come up with an idea how to solve this? Me running coLinux makes it next to impossible to just switch to another (yama-enabled) kernel, but I do use Ubuntu 11.10 which seems to silently rely on the fact that there is just such a kernel running underneath it. In my case it isn’t.
$ uname -a Linux andLinux 2.6.33.7-co-0.7.10-r1588 #1 PREEMPT Mon Aug 8 04:13:31 UTC 2011 i686 athlon i386 GNU/Linux
$ cat /etc/motd | head -n 1 Welcome to Ubuntu 11.10 (GNU/Linux 2.6.33.7-co-0.7.10-r1588 i686)
Hi Alexander Kriegisch (kriegaex),
I don’t have any problem with it on Ubuntu 12.04 (speedlinux) Did test with this example: http://monkeypatch.me/blog/move-a-running-process-to-a-new-screen-shell.html
uname -a Linux speedLinux 2.6.33.7-co-0.7.9-r1581 #1 PREEMPT Sat Apr 9 21:08:08 UTC 2011 i686 athlon i386 GNU/Linux cat /etc/motd | head -n 1 Welcome to Ubuntu precise (development branch) (GNU/Linux 2.6.33.7-co-0.7.9-r1581 i686)
Best regards, Johann Pascher
Brilliant. Thanks a lot!
That;s a really cool tool man, why don;t you try to put that in the Fedora repository or some other linux repository.
@Kot It’s actually been in (at least) Fedora, Debian, and Ubuntu for a while now. http://packages.debian.org/search?keywords=reptyr https://admin.fedoraproject.org/pkgdb/acls/name/reptyr
Please add support for reptyr’ing a process and all of its children. I had a use for this today, but luckily caught the note about children processes before I messed up my 6hr build. Even though Neercs and the like exist, your tool is light-weight and works without having to install a whole client/server framework. It’s a great swiss-army knife style utility that makes Linux so great!
Support for grabbing the child processes would be very nice (e.g. for grabbing a running bash script) – I could see using that quite often.
Presumably the basic idea would be to simultaneously suspend the process and all its children (any way to use process groups?), individually grab each one, then resume them all?
@Mike Grant Yeah, that is basically right. There are some complications around dealing with zombie children, but I think in most cases it should be pretty straightforward.
Thanks for creating this, it’s great! I can move mpg123 around from terminal to termina!
Does anyone have ptrace files for MIP architecture?
Thanks in advance.
@Vinod Typo: MIPS architecture only