[comp.unix.aux] Changing Control Terminal in A/UX

km@mathcs.emory.edu (Ken Mandelberg) (06/26/89)

A/UX appears to only partially support the BSD semantics for control
terminals. Like BSD, it supports an extended version of setpgrp that
allows you to change the process group of a process into an existing
process group.

Unlike BSD, there is no provision for changing the control terminal of
a process to a tty that is already open. On BSD you can accomplish this
for example, with a TIOCNOTTY followed by an open of the desired
terminal (even if the terminal is already open).

In other words A/UX seems to strictly obey the System V semantics that
dictate that a proc's control terminal is either inherited from its
parent, or is associated with a terminal not already open (as it
establishes a new process group and becomes the process group leader).

This is not exactly what you want in a windowed environment.  It is
very possible that you may want to hang a process off of a pty
(associated with some window) that is already open and not inherited.
In A/UX you can put the proc into that process group, but not change
its control terminal.

Is there a trick that I am missing?

What does POSIX say on the issue? I notice that the BSD doc clearly
allow the control terminal change, the SunOS doc "currently" allows it,
and the System V doc disallows it.
-- 
Ken Mandelberg      | km@mathcs.emory.edu          PREFERRED
Emory University    | {decvax,gatech}!emory!km     UUCP 
Dept of Math and CS | km@emory.bitnet              NON-DOMAIN BITNET  
Atlanta, GA 30322   | Phone: (404) 727-7963

john@unisoft.UUCP (John Sovereign) (07/06/89)

In article <4174@emory.mathcs.emory.edu> km@mathcs.emory.edu (Ken Mandelberg) writes:
>... [in A/UX] there is no provision for changing the control terminal of
>a process to a tty that is already open. On BSD you can accomplish this
>for example, with a TIOCNOTTY followed by an open of the desired
>terminal (even if the terminal is already open).

BSD's behavior might be considered a security hole in need of plugging.

>In other words A/UX seems to strictly obey the System V semantics that
>dictate that a proc's control terminal is either inherited from its
>parent, or is associated with a terminal not already open (as it
>establishes a new process group and becomes the process group leader).

To be more precise, under A/UX, establishing the controlling tty via open(2)
requires these conditions: (a) the calling process is a "classical" process
group leader, i.e., its process id is equal to its process group id, (b)
the calling process is not currently controlled by any terminal, and (c) the
device being opened does not have a controlling process associated with it.

>Is there a trick that I am missing?

Yes.  You can muck with the device's idea of the controlling process,
using TIOCSPGRP.  The code below demonstrates this.

>What does POSIX say on the issue? I notice that the BSD doc clearly
>allow the control terminal change, the SunOS doc "currently" allows it,
>and the System V doc disallows it.

Unfortunately, your application is not supported by POSIX as currently
defined.  The POSIX functionality is similar to A/UX in this area, i.e.,
a System V base with (optional) job control support.  POSIX requires that
a controlling terminal be associated with only one session; the association
is determined by the session leader (IEEE Std 1003.1-1988 section 7.1.1.3).

Since session membership is defined by inheritance (section 3.1.2.2) and
the creation of a new session releases any associated controlling terminal
(section 4.3.2.2), only children of a session leader may be associated with
its controlling terminal.  Furthermore, the POSIX version of the BSD setpgrp(),
called setpgid(), is paranoid and does not allow processes to "join" process
groups in other sessions (section 4.3.3.4).

One of the really wonderful things (;-<) about POSIX is that it muddles the
the whole issue by making control-terminal allocation "implementation-defined".

>Ken Mandelberg      | km@mathcs.emory.edu          PREFERRED
>Emory University    | {decvax,gatech}!emory!km     UUCP 
>Dept of Math and CS | km@emory.bitnet              NON-DOMAIN BITNET  
>Atlanta, GA 30322   | Phone: (404) 727-7963

John "POSIX: The Final Solution" Sovereign	|	uunet!unisoft!john

			--- cut here ---
/*
 * attach:	attach a process to a tty (A/UX special),
 *		even if it already has a process associated with it
 *
 *		if there is a process already controlled by the tty,
 *		join its process group as well
 */

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

#define	CTRLTTY		"/dev/tty"

main(argc, argv)
int argc;
char **argv;
{
	int oldpgrp, newpgrp, fd, i;

	if (!argv[1]) {
		printf("usage: attach tty\n");
		exit(1);
	}

	/*
	 * use the BSD version of setpgrp
	 * and prevent TIOCSPGRP's from putting us in the background
	 */
	if (setcompat(COMPAT_BSDTTY|COMPAT_BSDSIGNALS|COMPAT_CLRPGROUP) == -1)
		perror("setcompat");

	/* record the tty's foreground process group */
	if ((fd = open(argv[1], O_RDWR)) == -1)
		perror(argv[1]);
	if (ioctl(fd, TIOCGPGRP, &oldpgrp) == -1)
		perror("GPGRP");
	(void) close(fd);

	/* unnecessary if there's no process associated with the tty */
	if (oldpgrp != 0) {
		newpgrp = 0;
		if ((fd = open(argv[1], O_RDWR)) == -1)
			perror(argv[1]);
		if (ioctl(fd, TIOCSPGRP, &newpgrp) == -1)
			perror("SPGRP 0");
		if (ioctl(fd, TIOCGPGRP, &newpgrp) == -1)
			perror("GPGRP 0");
		(void) close(fd);
		if (newpgrp != 0)
			printf("tty pgrp is %d, not zero\n", newpgrp);
	}

	/* disconnect from the inherited controlling tty */
	if ((fd = open(CTRLTTY, O_RDWR)) == -1)
		perror("could not open CTRLTTY\n");
	if (ioctl(fd, TIOCNOTTY, 0) == -1)
		perror("TIOCNOTTY");
	(void) close(fd);

	/*
	 * make sure we're a "classical" process group leader
	 * after the TIOCNOTTY since it clears the pgrp
	 */
	if (setpgrp(0, getpid()) == -1)
		perror("setpgrp");
	if (getpid() != getpgrp())
		printf("not process group leader: pid = %d; pgrp = %d\n",
			getpid(), getpgrp());

	/* establish the controlling tty (we hope) */
	if ((fd = open(argv[1], O_RDWR)) == -1)
		perror(argv[1]);

	/* test that the above open() established the controlling tty */
	if (open(CTRLTTY, O_RDWR) == -1)
		perror("could not open /dev/tty");
	else
		printf("opened /dev/tty\n");

	/* unnecessary if there's no process associated with the tty */
	if (oldpgrp != 0) {
		/* join the old foreground process group */
		if (setpgrp(0, oldpgrp) == -1)
			perror("setpgrp(0, oldpgrp)");
		if (getpgrp() != oldpgrp)
			printf("still in own process group %d instead of %d\n",
				getpgrp(), oldpgrp);

		/* re-establish the foreground process group */
		if (ioctl(fd, TIOCSPGRP, &oldpgrp) == -1)
			perror("SPGRP");
		if (ioctl(fd, TIOCGPGRP, &newpgrp) == -1)
			perror("GPGRP");
		if (newpgrp != oldpgrp)
			printf("expected to reset tty pgrp to %d; still %d\n",
				oldpgrp, newpgrp);
	}

	/* verify process group membership by a keyboard interrupt */
	pause();
}