[comp.unix.wizards] how do I make a process release its terminal?

paul@vixie.UUCP (Paul Vixie Esq) (01/15/87)

I have a program that forks itself and exits upon running -- its child
process runs as a daemon forever after.  In 'ps aux', I notice that it
still shows my tty as its control terminal -- even if I log out and
back in.

I see that syslogd, inetd, cron, and the rest do NOT show 'co' as their
control terminal; how do I make this happen for me?

This is (more or less) BSD 4.2, and the program is setuid root.  Thanks...
-- 
Paul A. Vixie        {ptsfa, qantel, crash, winfree}!vixie!paul
329 Noe Street       dual!ptsfa!vixie!paul@ucbvax.Berkeley.EDU
San Francisco        nike!ptsfa!vixie!paul@seismo.CSS.GOV
CA  94116            paul@vixie.UUCP     (415) 864-7013

wsr@lmi-angel.UUCP (01/21/87)

In article <> paul@vixie.UUCP (Paul Vixie Esq) writes:
>I see that syslogd, inetd, cron, and the rest do NOT show 'co' as their
>control terminal; how do I make this happen for me?

try this: 

#include <sys/ioctl.h>

fromlimbo ()
{
    int     f;

    if ((f = open ("/dev/tty", 2)) >= 0)
    {
	ioctl (f, TIOCNOTTY, 0);
	close (f);
    }
    else
	perror ("open");
}
-- 
Wolfgang Rupprecht	{harvard|decvax!cca|mit-eddie}!lmi-angel!wsr

m5d@bobkat.UUCP (01/26/87)

In article <453@vixie.UUCP> paul@vixie.UUCP (Paul Vixie Esq) writes:
>I have a program that forks itself and exits upon running -- its child
>process runs as a daemon forever after.  In 'ps aux', I notice that it
>still shows my tty as its control terminal -- even if I log out and
>back in.
>
>I see that syslogd, inetd, cron, and the rest do NOT show 'co' as their
>control terminal; how do I make this happen for me?
>
>This is (more or less) BSD 4.2, and the program is setuid root.  Thanks...
>-- 
>Paul A. Vixie        {ptsfa, qantel, crash, winfree}!vixie!paul
>329 Noe Street       dual!ptsfa!vixie!paul@ucbvax.Berkeley.EDU
>San Francisco        nike!ptsfa!vixie!paul@seismo.CSS.GOV
>CA  94116            paul@vixie.UUCP     (415) 864-7013

My guess is that the other daemons you mentioned are started in the
"rc" command file.  I think that "init" runs this with no standard file
descriptors, or maybe with /dev/null.  So far as I know, you can't get
rid of the control terminal.  You might try fiddling with the process
group, although the shell will grab the tty back anyway.

All this makes me wonder.  I don't have source; all the stuff I know
I've figured out through conjecture and the documentation.  If I ask a
question, and somebody who has source looks up the answer and posts it,
will the Unix police come and gun us all down?

--
****                                                         ****
**** At Digital Lynx, we're almost in Garland, but not quite ****
****                                                         ****

Mike McNally                                    Digital Lynx Inc.
Software (not hardware) Person                  Dallas  TX  75243
uucp: {texsun,killer,infotel}!pollux!bobkat!m5d (214) 238-7474

cudcv@warwick.UUCP (01/28/87)

In article <118@lmi-angel.UUCP> wsr@lmi-angel.UUCP (Wolfgang Rupprecht) writes:
|In article <> paul@vixie.UUCP (Paul Vixie Esq) writes:
|>I see that syslogd, inetd, cron, and the rest do NOT show 'co' as their
|>control terminal; how do I make this happen for me?
|
|try this:
|
|#include <sys/ioctl.h>
|
|fromlimbo ()
|{
|    int     f;
|
|    if ((f = open ("/dev/tty", 2)) >= 0)
|    {
|	ioctl (f, TIOCNOTTY, 0);
|	close (f);
|    }
|    else
|	perror ("open");
|}
|--
|Wolfgang Rupprecht	{harvard|decvax!cca|mit-eddie}!lmi-angel!wsr

I would have thought the 'else perror(...);' was undesirable.  If you can't
open /dev/tty it probably means you're already without a controlling terminal
and the ioctl is simply unecessary.  Don't you want a

        setpgrp(getpid());

after that, too, to fully dissociate it from the terminal (otherwise when you
hit ^C, you'll still get the signal).
-- 
UUCP:   ...!mcvax!ukc!warwick!cudcv	PHONE:  +44 203 523037
JANET:  cudcv@uk.ac.warwick.daisy       ARPA:   cudcv@daisy.warwick.ac.uk
Rob McMahon, Computing Services, Warwick University, Coventry CV4 7AL, England

mb@ttidca.UUCP (01/29/87)

In article <471@bobkat.UUCP> m5d@bobkat.UUCP (Mike McNally (dlsh)) answers
paul@vixie.UUCP (Paul Vixie Esq)'s question:

>>I see that syslogd, inetd, cron, and the rest do NOT show 'co' as their
>>control terminal; how do I make this happen for me?

with:

>				       So far as I know, you can't get
>rid of the control terminal.  You might try fiddling with the process
>group, although the shell will grab the tty back anyway.

Since the original question was about 4.2BSD, the answer is to use 
the TIOCNOTTY ioctl.

When we were converting from 4.1BSD to 4.2BSD, we had to take a
somewhat different tack.  We had an xns based network (without remote
login capability - normally used for file transfer only) on the 4.1
systems which we brought up on our then sole 4.2 system while
preparing to convert.  To make it convenient for users to try out 4.2
and still be able to work on 4.1 without first logging out from the
4.2 system, I put together an rlogin/rlogind style pair for the xns
based network, but had to deal with the control terminal problem,
which caused a number of problems (including some involving getpass(),
which still has, and probably always has had, a bug in the assumptions
it makes about what fdopen will do/return when passed a -1 file
descriptor).

I couldnt afford the downtime needed to add and test a new ioctl, so I
ended up using the following hack in the rlogind-like program on the
4.1 side:

	{				/* reset detach flag */

		struct proc p;
		int fd,t,pa;
		struct user *up =  (struct user *)( 0x80000000-UPAGES*NBPG);

		int tpgrp; /* turned out I didnt have to use this, but
			      on another system one might have to. */

		fd = open("/dev/kmem",2);

		lseek(fd,up->u_procp,0);
		read(fd,&p,sizeof(p));
		lseek(fd,up->u_procp,0);
		p.p_flag &= ~SDETACH;
		write(fd,&p,sizeof(p));
		close(fd);
	}	
	(void) close(slave);
	setdefaults(0);

While it did the job, it certainly was not a clean way to go.  Among
other things, it would have been better to just write p.pflag, rather
than all of p.  Aesthetically it was broken, but in behavior it wasn't,
so since nothing bad seemed to be happening, and there were lots of
other tasks at hand, I left it as it was, for the couple of weeks it
was needed.

In this case, I needed to be able to have a control terminal that was
the pty I was using.  Something similar to the above could probably be
used for related purposes as well.

guy@gorodish.UUCP (01/29/87)

>My guess is that the other daemons you mentioned are started in the
>"rc" command file.  I think that "init" runs this with no standard file
>descriptors, or maybe with /dev/null.  So far as I know, you can't get
>rid of the control terminal.

Well, yes, those other daemons are generally started from "/etc/rc"
or "/etc/rc.local".  However, most (if not all) such daemons manage
to detach themselves from their control terminal even if they're run
from such a terminal, so you *can* get rid of it.

>If I ask a question, and somebody who has source looks up the answer
>and posts it, will the Unix police come and gun us all down?

Not unless they notice it, it's in violation of somebody's trade
secret, and they decide to do something about it.  In this case, the
code was written at Berkeley and isn't covered by any trade secret
restrictions, so here it is:

#include <fcntl.h>
#include <sys/ioctl.h>

	...

	int tty_fd;

	tty_fd = open("/dev/tty", O_RDWR);	/* any open mode will do */
	if (tty_fd >= 0) {
		if (ioctl(tty_fd, TIOCNOTTY, 0) < 0)
			perror("Couldn't detach from controlling terminal");
		(void) close(tty_fd);
	}

That will be sufficient to detach you from your controlling terminal
and set your process group back to 0.

oattes@utmanitou.toronto.edu (Lee Oattes) (01/30/87)

In article <118@lmi-angel.UUCP> wsr@lmi-angel.UUCP (Wolfgang Rupprecht) writes:
>In article <> paul@vixie.UUCP (Paul Vixie Esq) writes:
>>I see that syslogd, inetd, cron, and the rest do NOT show 'co' as their
>>control terminal; how do I make this happen for me?
>
>try this: 
>    if ((f = open ("/dev/tty", 2)) >= 0)
>    {
>	ioctl (f, TIOCNOTTY, 0);
>	close (f);
>    }
>Wolfgang Rupprecht	{harvard|decvax!cca|mit-eddie}!lmi-angel!wsr

To this reply I would add that the following does a more complete job:

#include <sys/ioctl.h>
#include <sys/file.h>

	if( -1 == (fd = open("/dev/tty",O_RDWR)) ){
		fprintf(stderr,"could not open controlling terminal!\n");
		exit(-1);
	}
	if(  -1 == ioctl(fd,TIOCNOTTY,0) ) {
		fprintf(stderr,"could not unlink conrolling terminal!\n");
		exit(-1);
	}
	freopen("/dev/null","r",stdin);
	freopen("/dev/null","w",stdout);
	freopen("/dev/null","w",stderr);

This will remove the process from a "tty" and also close off the
standard connections to that "tty". We use this to run jobs from phone
lines which will continue to run after we log off.  If we do not remove
the job from the phone line "tty" they cause the kernel to think that
(correctly) the device is open and will not allow dialouts thru that
device.

Lee Oattes
{ihnp4!utzoo!, seismo!utai!, decvax!utcsri!, watmath!utai!} utmanitou!oattes

postman#@andrew.cmu.edu.UUCP (01/31/87)

ReSent-To:nntp-xmit#@andrew.cmu.edu
Return-path: <zs01#@andrew.cmu.edu>
To: outnews#ext.nn.comp.unix.wizards@andrew.cmu.edu,
	m5d@bobkat.UUCP (Mike McNally (dlsh)),
	paul@vixie.UUCP
In-Reply-To: <471@bobkat.UUCP>

I had a lot of fun trying to figure out process groups and control terminals
under BSD 4.2. There was this bug where I couldn't open /dev/tty in a certain
process (ENXIO error). So after spending a year looking for it in my code, I
got access to kernel sources. Here is what I found.

The kernel's idea of a process without a control terminal is one who's
process group is zero. It also has a pointer to the device, if this is NULL,
you don't have a control terminal. The documentation doesn't help here,
either you read the source or you lose. It says that if you have no control
terminal and you open another tty, it will become your control terminal. What
really happens is that if your process group is 0 and you open a tty, it
becomes your control terminal. If your process group is non-zero, and that
pointer is NULL, /dev/tty remains broken.

Getting rid of a control terminal is pretty easy. The following code
(strictly BSD 4.2) will do it:

#include <stdio.h>
#include <sys/file.h>
#include <ioctl.h>

void NukeControlTerminal()
{

    int fd;

    if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
        ioctl(fd, TIOCNOTTY, NULL);
        close(fd);
    }
    else
/* Insert real error handling here... */
        printf("Couldn't open control terminal, errno = %d\n", errno);
}

My bug was caused by setting the process group and then trying to open
/dev/tty. Since I really didn't have a control terminal (i.e. pointer was
NULL), it returned EXNIO. But when I opened another tty, it did not become
/dev/tty (because my process group was not 0).

There are a number of other problems you can get into by changing the process
group of a terminal (or of your process), and then trying to do an ioctl on
it. For example where you wish to fork a child, and have a terminal in the
child's process group but not in yours. If you do the process group switching
in the wrong order, you will not be able to ioctl the terminal into the
target process group...

Sincerely,
Zalman Stern
Information Technology Center
Carnegie Mellon University
zs01@andrew.cmu.edu

hedrick@topaz.UUCP (01/31/87)

One should perhaps note that after disinheriting your terminal by
using TIOCNOTTY, if you open any terminal, that becomes your
controlling terminal.  This can happen in unexpected ways.  Under 4.3
[more or less] we found that inetd was mysteriously getting connected
to the console.  This caused very odd results.  Like every time
somebody tried to log in via rlogin, they would get disconnected, and
whatever job happened to be logged in on the console would get logged
out.  Anyway, the problem turned out to be that inetd was calling
syslog, syslog was trying to open a network connection to loghost,
this was failing, and so syslog then opened the console.

guy%gorodish@Sun.COM (Guy Harris) (02/01/87)

>To this reply I would add that the following does a more complete job:

While you're at it, you might also want to ream out the environment;
you may not want to inherit the PATH, USER/LOGNAME, IFS, etc., etc.,
etc. values that the person who ran the daemon used.

guy@gorodish.UUCP (02/02/87)

>I would have thought the 'else perror(...);' was undesirable.  If you can't
>open /dev/tty it probably means you're already without a controlling terminal
>and the ioctl is simply unecessary.

True.  The standard idiom in all the daemons I've seen omits the
"perror".  You can, instead, check for errno == ENXIO, and only print
the error message if it's *not* ENXIO, since the "/dev/tty" driver
returns ENXIO if you don't have a controlling terminal.

> Don't you want a
>
>        setpgrp(getpid());
>
>after that, too, to fully dissociate it from the terminal (otherwise when you
>hit ^C, you'll still get the signal).

Nope.  First of all, "setpgrp" in 4BSD takes two arguments, so it
would be

	setpgrp(0, getpid());

and second, TIOCNOTTY *really* disassociates you from the terminal,
so it's not necessary.  TIOCNOTTY sets your process group ID to 0.