tim@brspyr1.BRS.Com (Tim Northrup) (01/28/89)
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? 2. ioctl(...TIOCSPGRP...) Is using ioctl to set the process group any different than using the setpgrp() system call? 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 <sys/ioctl.h> on a BSD system. 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? Thanks in advance for any assistence. -- Tim -- Tim Northrup +------------------------------------------+ +---------------------------------+ GEnie: T.Northrup | UUCP: uunet!steinmetz!brspyr1!tim | Air Warrior: "Duke" | ARPA: tim@brspyr1.BRS.Com +------------------------------------------+
guy@auspex.UUCP (Guy Harris) (01/28/89)
>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). Well, it depends on which flavor of UNIX you're using.... > 1. setpgrp(2) > > This is the method described in the System V section > termio(7), and does seem to work. One would hope it would, since that's the call you're supposed to use for that in S5. > 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. That's because "setpgrp" *isn't* the call you're supposed to use on BSD systems. "setpgrp" has the side effect of detaching you from your controlling terminal on S5, but it doesn't have that side effect on BSD. (The two were invented independently - at least in part - and for different purposes; the BSD one is used for job control, and processes are supposed to keep their controlling tty no matter whether they're in the foreground or in the background.) > 2. ioctl(...TIOCSPGRP...) > > Is using ioctl to set the process group any different > than using the setpgrp() system call? Yes, the system call sets the process group of a *process*, while the "ioctl" (not available currently on vanilla S5) sets the process group of a *terminal*. Basically, if you type something like your interrupt character (RUBOUT, ^C, whatever), the terminal driver sends a signal to the process group to which it belongs, which means it's delivered to all processes that are currently in that process group. > 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 <sys/ioctl.h> on a BSD system. That's what "void tty association" means - "void" a process's "association" with its controling "tty", i.e. detach itself from that tty. ("Void where prohibited by law", on the other hand, is a command telling you to urinate, say, on the White House lawn. :-)) The 4.3-tahoe TTY(4) manual page says (I think, earlier 4.3BSD manuals and maybe the 4.2BSD manual said something similar): A process can remove the association it has with its controlling terminal by opening the file "/dev/tty" and issuing an "ioctl(f, TIOCNOTTY, 0);" This is often desirable in server processes. > 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? It doesn't detach you from your controlling terminal, that's for sure.... > Should I do this in concert with any/all of the > above options? Yes, you might want to do this if your program is, say, a daemon process that's trying to run "cleanly" regardless of whether it's started from "/etc/rc" or from a user's terminal; that way, it won't "hang on" to the terminal (i.e., it won't hold it open, and won't issue messages to it). Dave Lennert (formerly of HP, now of Sequent - right, Dave?) wrote a fairly detailed paper on "how to write a daemon", going into details about these sorts of things on various UNIX flavors. I think it appeared in one of the UNIX magazines within the past few months.
rsalz@bbn.com (Rich Salz) (01/29/89)
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).
In <908@auspex.UUCP> guy@auspex.UUCP (Guy Harris) writes:
Well, it depends on which flavor of UNIX you're using....
Dave Lennert (formerly of HP, now of Sequent - right, Dave?) wrote a
fairly detailed paper on "how to write a daemon", going into details
about these sorts of things on various UNIX flavors. I think it
appeared in one of the UNIX magazines within the past few months.
Dave's article (which is excellent!) appaeared in \fI;login:\fP
the Usenix newsletter. Volume 12, number 4 (July/August 1987).
Back issues might be available, contact uunet!usenix!office.
/rich $alz
--
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
lm@sun.uucp (Larry McVoy [Contractor]) (01/30/89)
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 <sys/ioctl.h> 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 <sys/file.h> 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.
steinar@fdmetd.uucp (Steinar Overbeck Cook) (01/30/89)
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). .......... There is an excelent article about this in the December issue of UnixWorld. The article is named "How to write UNIX deamons", the author of the article is Dave Lennert. ------------- Steinar Overbeck Cook Fellesdata a.s P.O. Box 248 0212 OSLO 2 NORWAY Phone : +47 2 52 80 80 Fax : +47 2 52 85 10 E-mail : ...!mcvax!ndosl!fdmetd!steinar or steinar@fdmetd.uucp
guy@auspex.UUCP (Guy Harris) (01/31/89)
> /* > * TIOCNOTTY will get rid of my tty & set my pgrp to 0 > */ > ioctl(0, TIOCNOTTY, 0); RU sure? I think TIOCNOTTY only works on "/dev/tty" - it's caught not by the standard line discipline, say, but by the "/dev/tty" driver. >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: Note that you have to do those operations *after* you've opened the tty in question, and gotten it as a controlling tty; doing it before won't help. >POSIX: > Do a setsid(). Or use the O_NOCTTY flag when opening the tty, which prevents "open" from making it a controlling tty in the first place.
lm@snafu.Sun.COM (Larry McVoy) (01/31/89)
>I wrote: >> /* >> * TIOCNOTTY will get rid of my tty & set my pgrp to 0 >> */ >> ioctl(0, TIOCNOTTY, 0); Guy wrote: >RU sure? I think TIOCNOTTY only works on "/dev/tty" - it's caught not >by the standard line discipline, say, but by the "/dev/tty" driver. I say: As usual, oh Master, little grasshopper has made mistake. So solly. Do it thusly: fd = open("/dev/tty", 2); if (fd != -1) ioctl(fd....) >>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: > >Note that you have to do those operations *after* you've opened the tty >in question, and gotten it as a controlling tty; doing it before won't >help. Indeed, quite correct. >>POSIX: >> Do a setsid(). > >Or use the O_NOCTTY flag when opening the tty, which prevents "open" >from making it a controlling tty in the first place. Unfortunately, the most common case is that you have gotten your ctty from login or whatever, so the NOCTTY doesn't help. Larry McVoy, Lachman Associates. My opinions are that.
jgy@opus.ATT.COM (John Young) (02/01/89)
A additional comment regarding process groups on SVR3.1 and up. A change to setpgrp() makes it only effective the first time. This means that after a tty link first goes down (and you get your first SIGHUP) there is no way to get further SIGHUP indications (by the same process). That's progress for you! If anyone knows otherwise please inform me.