How to divorce a process from the controlling tty (was syslogd.c)

Bruce Perens bp at pixar.UUCP
Tue Jun 12 02:48:05 AEST 1990


In article <1990Jun8.070904.7466 at athena.mit.edu> jik at athena.mit.edu (Jonathan I. Kamens) writes:
>
> Pulled from etc/syslogd.c:
>
>	if (!Debug) {
>		if (fork())
>			exit(0);
>		for (i = 0; i < 10; i++)
>			(void) close(i);
>		(void) open("/", 0);
>		(void) dup2(0, 1);
>		(void) dup2(0, 2);
>		untty();
>	}
>

keany at sequent.UUCP (Bernard Keany) writes:
>"Change the working directory to /.

Sorry, you didn't read the example carefully. There is no directory
changing going on in this code fragment.

The example is trying to divorce the process from its controlling
tty, so that it will not be hit by the side-effects of being associated
with a tty. To do this, it closes any fds that might be open to
a tty, and opens 0, 1, and 2 to _anything_ that it is sure is not a tty.
Then it calls untty(), which does an ioctl that clears the processes
controlling tty.

I think the usage of / for this is descended from a version of unix so
old that it did not have /dev/null . I don't believe in programs that
do this "because /dev/null might not exist". It might be that the programmer
wants the fd open to something that will cause an error on _write_, but
I don't buy that either. This code would fail if
some system didn't allow one to open / as a regular file, which I feel
is more likely than /dev/null going away, especially in the context of
network filesystems.

Some side effects from being associated with a controlling tty that
daemons want to avoid are:
	blocking on read or _write_, possibly forever.
	interrupt, quit, and other signals sent by the tty.
	shells mucking with your process group, and sending signals.
	(feel free to add to this list)

I the blocking part is more important than you might think. A while
ago I found the console of a 4.2 system locked up (by a spurious control-S)
and two dozen processes blocked on console writes.

Versions of unix that I have played with set the controlling tty this
way, your mileage may vary: If your parent has one, you get that one.
Otherwise, the first time you do an open on a tty, that becomes your
controlling tty.

Here's how I would divorce a process from a controlling tty. Feel
free to criticize and correct this:

	int	count;
	int	null_fd;
	int	tty_fd;
	/*
	 * Get rid of ALL open fds. Any one of them might be open
	 * to a tty.
	 */
	count = NFDS;
	do {
		close(--count);
	} while ( count );

	/*
	 * Open the traditional fds for a tty connection to
	 * something, to reduce the chance that they might be opened
	 * to a tty by some code later on.
	 */
	null_fd = open("/dev/null", 0);
#ifdef PARANOID
	if ( null_fd < 0 )
		null_fd = open("/", 0); /* /dev/null might not exist */
#endif
	dup2(0, 1);
	dup2(0, 2);

	/*
	 * Here's the fun part. See if there is still a controlling
	 * tty by opening /dev/tty. If you don't have a controlling
	 * tty, the open will fail! If it succeeds, do ioctl TIOCNOTTY
	 * on that fd, and close it again.
	 */
	 if ( (tty_fd = open("/dev/tty", 0)) >= 0 ) {
		ioctl(tty_fd, TIOCNOTTY, 0);
		close(tty_fd);
	 }

Now, don't go and open the console, or you might spoil everything.
Use syslog for logging.

Someone on the net will doubtless be able to correct my own misconceptions
and typos.
					Bruce Perens



More information about the Comp.unix.wizards mailing list