Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!nrl-cmf!ames!oliveb!sun!lm From: lm@sun.uucp (Larry McVoy [Contractor]) Newsgroups: comp.unix.wizards Subject: Re: Proper way to detach from control terminal? Keywords: detach control-terminal setpgrp Message-ID: <87445@sun.uucp> Date: 30 Jan 89 00:48:33 GMT References: <5333@brspyr1.BRS.Com> Reply-To: lm@sun.com (Larry McVoy) Organization: Sun Microsystems, Mountain View Lines: 227 In article <5333@brspyr1.BRS.Com> tim@brspyr1.BRS.Com (Tim Northrup) writes: >I would like to know if there is a "proper" way to detach a process from >its control terminal (once it has fork'ed and the parent has >terminated). I have seen a couple of different ways of doing this and >would like to know which ones work, which don't, if there are any >others, and which is the correct way to do it. > >Here are the methods I have seen: > > 1. setpgrp(2) > > This is the method described in the System V section > termio(7), and does seem to work. The calling sequence > I am using is simply setpgrp() for System V and > setpgrp(0,getpid()) for BSD based code. But, on BSD > systems, the control terminal is still reported when a > ps(1) is done. Under System V, the control tty is > listed as '?', as I would expect. Is there any way to > get this behavior under BSD? This is good for System5.3 and earlier models. This is bad for BSD systems. See the ``whole story'' below. > 2. ioctl(...TIOCSPGRP...) > > Is using ioctl to set the process group any different > than using the setpgrp() system call? This is different from above in that it sets the ttys' pgrp - not yours. > 3. ioctl(...TIOCNOTTY...) > > I have seen this used in the recently posted plp > software, but cannot find any documentation on this > setting other than "void tty association" in the > header file on a BSD system. This is best for BSD systems. > 4. closing all terminal files > > I don't know if I am reading things wrong, but it seems > that some programs simple close stdin/stdout/stderr on > startup and open something else as stdin. Does this > really do anything? Should I do this in concert with > any/all of the above options? This does not do what you want. =============================================================================== I've been working on this stuff for the last month while POSIXifying SunOS. I think I have a handle on it & it's a mess. You do different things depending upon which system you are on. Suppose we have the following manifests: SYS_5_3 /* all System5 like versions up to 5.3 */ BSD_4_3 /* all BSD systems up to 4.3 & 2.10 (not sure on 2.x) */ POSIX /* any POSIX conforming system, i.e. BSD 4.4,Sys5.4,SunOS4.1 */ and suppose that you have a daemon that you wish to disassociate from the ctty: #if defined(POSIX) /* * setsid will give me a new session w/o any tty associated at all */ setsid(); #elif defined(BSD_4_3) /* * TIOCNOTTY will get rid of my tty & set my pgrp to 0 */ ioctl(0, TIOCNOTTY, 0); #elif defined(SYS_5_3) /* * system 5 setpgrp is very similar to POSIX setsid */ setpgrp(); # endif The problem comes when you don't want a ctty but you do want to talk to a tty. Open() has the unfortunate side effect of handing out cttys' even when you don't want one. You can do the following in the specified environment: BSD4.3 & earlier: TIOCNOTTY on a fd that is talking to your tty. This is commonly done like this: if ((fd = open("'dev/tty", 2)) != -1) { ioctl(fd, TIOCNOTTY, 0); close(fd); } System5.3 & earlier: Do a setpgrp(). POSIX: Do a setsid(). =============================================================================== The whole story: The system keeps track of tty's in several places; the u area, the open file table, and in the tty driver. Open file table: doesn't care about ctty distinctions, it's just a descriptor. tty driver: remembers the process group associated with the tty - this is Sys5: the shell & all its' children BSD: the foreground job[s]; this is the shell or the current cmd. u area: u_ttyp, u_ttyd, and u_tty[iv]p. These all contain information that is specific to the ctty. short *u_ttyp a pointer to the process group field in the tty driver dev_t u_ttyd device # of the ctty vnode *u_ttyvp vnode of the ctty So, how do you know if there's a ctty for a process? If u_ttyd != 0 then this process has a ctty. This is how /dev/tty works, by the way, it looks at u_tty* to find out what /dev/tty really is. Controlling tty's are used for signal handling. The tty driver needs to know who gets signaled when control chars come in; this is why it remembers the (foreground) process group of the tty. Let's consider each of the methods outlined above, what they do, and how effective they are: 1. setpgrp(2) System5.3 & earlier: This will release your controlling tty (zero u_tty*), place you in a new process group (pgrp = pid), and (maybe) note that you are a process group leader. Any references to the tty in the open file table are fine, you've still got them. You no longer are vulnerable to signals from that tty. Also /dev/tty access no longer works. This is the ``right way'' to do it under System5.3 and similar systems. BSD: This will only set p_pgrp. It does nothing about your ctty (u_tty* are untouched). It is a way of insulating yourself from signals from the ctty but the ctty is still there, the ttys' pgrp is still in the tty driver, etc. This is not an acceptable method under BSD, it leaves things messy. 2. ioctl(...TIOCSPGRP...) BSD only: This sets the tty drivers idea of who is the foreground process group. This is intended for use by the controlling shell in a job control environment, it is not a method of relinquishing your ctty. 3. ioctl(...TIOCNOTTY...) BSD only: This identical to a sys5 setpgrp() with the following difference: p_pgrp is set to 0 instead of p_pid. It's the best way to get rid of a ctty under BSD4.3 and earlier systems. 4. closing all terminal files This closes the entry in the open file table; it does not do anything with the ctty information. I wasn't sure about this so I tried it out: this prints out the hello: #include main() { int i; close(0); close(1); close(2); i = open("/dev/tty", O_RDWR); if (i != -1) write(i, "Hello\n", 6); } =============================================================================== Something else to consider is how open() knows to hand out ctty's. Again, it depends on your environment: BSD4.3 and earlier: On these systems you get a ctty on open when your p_pgrp was 0. It had the side effect of giving you a process group as well; if the tty had no process group, you got your pid as a pgrp, otherwise you ``joined'' the ttys' process group. I believe that this joining business is a security hole. System5.3 & earlier: You needed to be a process group leader, not already have a ctty, and the tty cannot already have a process group. The first two conditions would be satisfied if you had just done a setpgrp(). The third was a security mechanism intended to make sure that the tty was not being shared illegally. System5 is more secure than BSD in this respect. POSIX (4.4BSD, Sys5.4, SunOS4.1) You need to be a session leader, not have a ctty, and the tty cannot belong to any other session. The first two would be satisfied if you had just done a setsid(). The last is a security measure a la Sys5. Another note about how this changes under POSIX: controlling tty's are really a per session concept - this business about each process knowing about its' ctty in the u area is wrong. POSIX has a session concept and it is implemented (in SunOS4.1) as a struct; the proc struct points to it. So you end up with: OLD NEW --- --- u_ttyd u_procp->p_sessp->s_ttyd u_ttyp u_procp->p_sessp->s_ttyp u_ttyvp u_procp->p_sessp->s_ttyvp All processes in the same session point to the same session struct. Note that ttyd is really ttyvp->v_rdev; it's been kept because a lot of code assumes that u_ttyd == 0 means no ctty; in our case u_ttyvp (aka s_ttyvp) == NULL means no ctty. If there's anyone out there that can point out mistakes I've made I'd like to hear about it. Or if you have comments, questions, clarifications, send me mail. I hope this helps.