Somebody mentioned reptyr (https://github.com/nelhage/reptyr) in chat tonight, and so I was thinking about how one might do it properly; this is a topic I've looked at in the past from time to time, so I think I have a viable plan for it. Which I'm going to post, both for feedback and in the hope that I can trick someone into implementing it for me. :-)
(1) It seems clear that the unit of transfer should be a whole process group (or "job"); if you want to move your mailreader, it makes sense for the editor it spawned to go with it, and so on. (2) It also seems desirable as part of the same machinery to be able to reconnect to jobs that have been lost because of a disconnect. For that and some technical reasons I think moving a job should have two parts: first detaching it from one tty and then attaching it to another. It follows from Occam's razor that a job that's been detached but not yet reattached should be in the same state as a background job that's been hung up on; formally, an orphaned process group. (3) Therefore, there are two actions. At the shell level, this should look something like % detach %1 [1] Detached (pid 12345) mutt % jobs % and % attach 12345 [1] + Suspended mutt % (4) At the syscall level, we add two ioctls: TIOCDETACH and TIOCATTACH. Both are called on one's controlling tty and take a process group id as the argument. TIOCDETACH does the following: - checks that the target process group is in the session of the TIOCDETACH caller; - for any process in the process group whose parent pid is outside the process group, sets the parent pid to 1; - creates a new session with no session leader; - moves the process group into the new session and out of its old session; - for every process in the process group, goes through the process's file table and collects a list of the unique open files connected to the old session's controlling tty; - for each of these clones the open file, except connects the open file to a dummy tty device established for the purpose; - goes through the process file tables again and replaces the old open files with the corresponding new ones; - sends SIGCONT to the process group. TIOCATTACH does the following: - checks that the caller is a session leader (maybe not necessary); - checks that the target process group is in a session with no session leader; - checks that either the caller has euid 0, or at least one of the uids of every process in the process group matches the caller's euid (*); - for any process in the process group whose parent pid is 1, changes it to the caller's pid; - moves the target process group into the caller's session; - disposes of the target process group's old session if it's now empty; - for every process in the process group, goes through the process's file table and for every open file attached to the dummy tty device, reconnects it to the new session's controlling tty instead; - sends SIGTSTP to the process group. (*) This security check is sufficient but not necessary; it e.g. prohibits moving sessions that contain su instances. It would probably be better to attach credentials to process groups and check those instead; but that's a bigger departure from standard behavior and should probably be done separately. (But until then, it's probably a good idea to disallow detaching process groups that can't be reattached.) Also, we'll change the hangup-time behavior so it also does the dummy tty device step, so orphaned process groups that have been hung up on are the same as process groups that have been explicitly detached. (Another way to do this is to keep the same hangup behavior we have now and have TIOCDETACH mimic it; however, then it becomes harder to identify which open files need to be reconnected to the new tty at attach time.) The dummy tty device is basically a copy of /dev/null that accepts tty ioctls. To implement detach, the shell calls TIOCDETACH and forgets about the job it detached. To implement attach, the shell calls TIOCATTACH and adds the necessary goop to its own data structures so that it doesn't get confused when that job starts reporting status to it. It might be necessary for TIOCATTACH to provide a list of pids attached, or at least pids attached that have been reparented to the caller, in order for shells to be able to do this properly. Or maybe provide a separate call to get this information. It should not be necessary to touch wait(), which is good because that code in netbsd is a maze. Have I forgotten anything necessary to make this work? (At least from at semantic level; I'm sure there are a pile of implementation issues.) -- David A. Holland dholl...@netbsd.org