[comp.bugs.sys5] SysVR3.2 Setpgrp behaviour

root@oid.UUCP (Admin-P.L. Aeten) (10/11/89)

Having to write a file transmission program which was to be launched after some
tape processing was done I was confronted with what I consider to be abnormal
behaviour of 'setpgrp'. Since the tying up of a terminal (operators terminal
using menu interface) was unacceptable (yes background (&) was possible but
the need to know was overwhelming...well read on.

It seems the "setpgrp" call behaves as designed and as per documentaion. However
if followed by a 'while' or 'for' (finite or infinite) loop it will not detach
a process from the controlling tty. I've tried both. Following are two somewhat
degenerate code fragments.

	char *progname;

	main(argc, argv)
	char *argv;
	int argc;
	{
		.
		.
		.
		if( setpgrp() == FAIL )
		{
		   perror("Detach failed: ", progname);
		   exit(2);
		}

		for( cnt = 0; cnt < 2; cnt++)
		{
		   do something
		}

		.
		.
	exit(some val);
	}

The above will tie up the terminal invoked from as does the following:

	char *progname;

	main(argc, argv)
	char *argv;
	int argc;
	{
		.
		.
		.
		if( setpgrp() == FAIL )
		{
		   perror("Detach failed: ", progname);
		   exit(2);
		}

		while(somecondition)
		{
		   do something
		}

		.
		.
	exit(some val);
	}

In order to free up the terminal on the 'setpgrp' I had to arrange the
demise of the parent prior to the call.

		.
		.
		.
		.
		.

		if( pid = fork())
		{
		    if( pid == -1)
			{
			  perror("Can't fork process: ", progname);
		 	  exit(2);
			}
		    exit(0);
		}

		umask(022);

		setpgrp();
		.
		.
		.
		
[Additional information: I closed all filedes associated with the terminal
but to no avail... SysVR2.1.2 with V3.1 compiler exhibits the same condition]

I am at a complete loss to explain this. Being a curious sort I can't let
this one get by. Can anyone shed some light on why this occurs? Is the
compiler (AT&T CPLU 4.2) at fault or is this not a defect? Or am I doing
something wrong?

Thank you all!
Peter


P. L. Aeten
Manager - UNIX Production Systems
VU/TEXT Information Services

{attmail,dsinc,netsys,lll-winken}oid!paeten
Bus [215-574-4477] or Home [215-461-5540]

cpcahil@virtech.UUCP (Conor P. Cahill) (10/13/89)

In article <1970@oid.UUCP>, root@oid.UUCP (Admin-P.L. Aeten) writes:
> Having to write a file transmission program which was to be launched after some
> tape processing was done I was confronted with what I consider to be abnormal
> behaviour of 'setpgrp'. Since the tying up of a terminal (operators terminal
> using menu interface) was unacceptable (yes background (&) was possible but
> the need to know was overwhelming...well read on.

> It seems the "setpgrp" call behaves as designed and as per documentaion. However
> if followed by a 'while' or 'for' (finite or infinite) loop it will not detach
> a process from the controlling tty. I've tried both. 

It *appears* from your post that you interpret "detach a process from the 
controlling tty" as letting the program continue running while you have 
control of the tty to perform other operations. 

The setpgrp() program only places the current program into a different
process group and disconnects the link from the process to the controlling
tty.  Disconnecting the link from the controlling tty means that signals (like
SIGHUP) generated in the tty driver will not be delivered to this process and
open(/dev/tty) will fail, since this program is no longer connected to a tty.

However, what you want to do is place your program in the background.  Yes, 
you can do that from the command line (as you noted with the &) or you can
have the program do it itself (as you final example does).  This is the only
mechanism available under UNIX to do what you want to do.

This is true for all UNIXs (that I have seen).


PS-> you should still do a setpgrp() after the fork to ensure that your 
new program does not recieve signals from the terminal.
-- 
+-----------------------------------------------------------------------+
| Conor P. Cahill     uunet!virtech!cpcahil      	703-430-9247	!
| Virtual Technologies Inc.,    P. O. Box 876,   Sterling, VA 22170     |
+-----------------------------------------------------------------------+

chip@ateng.com (Chip Salzenberg) (10/14/89)

According to cpcahil@virtech.UUCP (Conor P. Cahill):
>The setpgrp() program only places the current program into a different
>process group and disconnects the link from the process to the controlling
>tty.

Quite.

>PS-> you should still do a setpgrp() after the fork to ensure that your 
>new program does not recieve signals from the terminal.

You should also do another fork() AFTER the setpgrp().  Otherwise, the child
process remains a process group leader.  And if a process group leader
without a controlling tty opens a tty that no other process has open at the
time [inhale], then that tty becomes the process's controlling tty.  So the
child must fork() again; then the grandchild process is a safe daemon.

Finally:

	/*
	 * Daemon code.
	 * Insert error checking to taste.
	 */

	if (fork() != 0)
		exit(0);
	setpgrp();
	if (fork() != 0)
		exit(0);

	/*
	 * And away we go...
	 */

Personally, I think this whole "setpgrp does two jobs" is a grody hack.
It's as bad as the "setuid(getuid()) is reversible for all setuid programs
except setuid-root programs" grody hack.  Well, maybe not that bad.
-- 
You may redistribute this article only to those who may freely do likewise.
Chip Salzenberg at A T Engineering;  <chip@ateng.com> or <uunet!ateng!chip>
"'Why do we post to Usenet?'  Naturally, the answer is, 'To get a response.'"
                        -- Brad "Flame Me" Templeton

karish@forel.stanford.edu (Chuck Karish) (10/15/89)

In article <253664A4.20995@ateng.com> chip@ateng.com (Chip Salzenberg) wrote:

>Personally, I think this whole "setpgrp does two jobs" is a grody hack.

How about setsid()?  It does three jobs: setpgrp()'s two, plus
making the caller the foreground process.

	Chuck Karish		karish@mindcraft.com
	(415) 493-9000		karish@forel.stanford.edu

kremer@cs.odu.edu (Lloyd Kremer) (10/16/89)

In article <253664A4.20995@ateng.com> chip@ateng.com (Chip Salzenberg) writes:

>You should also do another fork() AFTER the setpgrp().  Otherwise, the child
>process remains a process group leader.  And if a process group leader
>without a controlling tty opens a tty that no other process has open at the
>time [inhale], then that tty becomes the process's controlling tty.  So the
>child must fork() again; then the grandchild process is a safe daemon.
>
>Finally:
>
>	/*
>	 * Daemon code.
>	 * Insert error checking to taste.
>	 */
>
>	if (fork() != 0)
>		exit(0);
>	setpgrp();
>	if (fork() != 0)
>		exit(0);
>
>	/*
>	 * And away we go...
>	 */


Do you need two forks?  Wouldn't the "second" fork alone be sufficient?

Example:
	setpgrp();  /* original parent divorces controlling tty and
		       becomes process group leader */
	close(0);
	close(1);
	close(2);  /* sure, why not? */
	if(fork() != 0)
		exit(0);  /* death of parent backgrounds the process */
	/* child process will be non-process-group-leader */

	/* And away we go... */

-- 
					Lloyd Kremer
					...!uunet!xanth!kremer
					Have terminal...will hack!

chip@ateng.com (Chip Salzenberg) (10/18/89)

According to kremer@cs.odu.edu (Lloyd Kremer):
>According to chip@ateng.com (Chip Salzenberg):
>>You should also do another fork() AFTER the setpgrp().  Otherwise, the child
>>process remains a process group leader.  And if a process group leader
>>without a controlling tty opens a tty that no other process has open at the
>>time [inhale], then that tty becomes the process's controlling tty.  So the
>>child must fork() again; then the grandchild process is a safe daemon.
>
>Do you need two forks?  Wouldn't the "second" fork alone be sufficient?

Unfortunately, the first fork() is required to ensure that setpgrp() will
work correctly.  You see, setpgrp() has two functions:

    1.  Make the process a process group leader.
    2.  Make the process have no controlling tty.

Here's the ugly part:  If function #1 isn't needed -- i.e. if the process is
already a process group leader -- then function #2 is _not_ performed.  In
other words, if a process is a process group leader, setpgrp() does nothing.
(Sigh.)

So summarize:  The fork() _before_ the setpgrp() guarantees that the child
isn't a process group leader, and thus ensures that setpgrp() will work,
losing the controlling tty.  The fork() _after_ the setpgrp() guarantees
that the grandchild won't ever get a controlling tty by accident.

Now you see why I said that setpgrp() is a grody hack.  :-(
-- 
You may redistribute this article only to those who may freely do likewise.
Chip Salzenberg at A T Engineering;  <chip@ateng.com> or <uunet!ateng!chip>
"'Why do we post to Usenet?'  Naturally, the answer is, 'To get a response.'"
                        -- Brad "Flame Me" Templeton