Proper way to detach from control terminal?
Larry McVoy [Contractor]
lm at sun.uucp
Mon Jan 30 10:48:33 AEST 1989
In article <5333 at brspyr1.BRS.Com> tim at 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.
More information about the Comp.unix.wizards
mailing list