[comp.unix.wizards] Proper way to detach from control terminal?

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.