Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!bloom-beacon!mit-eddie!mit-amt!mit-caf!vlcek From: vlcek@mit-caf.MIT.EDU (Jim Vlcek) Newsgroups: comp.unix.wizards Subject: Re: Strange SUN behaviour. Keywords: killing, ctrl-d, sun3, OS4. Message-ID: <3278@mit-caf.MIT.EDU> Date: 8 Oct 89 21:16:26 GMT References: <703@hrc63.uucp> Reply-To: vlcek@mit-caf.UUCP (Jim Vlcek) Organization: Microsystems Technology Laboratories, MIT Lines: 83 Mr. P Johnson "Baddow" is having problems with a process which, when killed, takes its parent shell with it. He gets a stream of "Use 'logout' to log out" messages just before the parent dies. The processes which exhibit this behavior use a lightweight process package which performs some signal management for non-blocking i/o. This is all going on under SunOS, and he's wondering what's going wrong. It's the non-blocking i/o that's doing it - I've run into the same thing working under 4.3BSD, and I assume SunOS does non-blocking terminal i/o in the same way. When you set up the terminal for non-blocking i/o, probably with a line like res = fcntl(fd, F_SETFL, FNDELAY); as it would be in 4.3BSD, subsequent read()s return -1 with errno set to EWOULDBLOCK if no input is available to be read. This is in contrast to the default action of blocking until input is available. The problem is, this level of terminal attribute is unique to the terminal - not to the process which sets the attribute. Thus, if a process sets up a terminal for nonblocking i/o using FNDELAY, and is subsequently terminated without resetting, read()s performed by the parent shell on the same terminal will return spurious EOF conditions. Since the read()s executed by the shell do not block anymore, the effect is the same as a (very fast) stream of ^Ds being sent from the terminal. Eventually, the shell tires of receiving these EOF conditions, and exits. This will happen if the process which set the non-blocking i/o is interrupted (with, say, ^Z) or terminated abruptly by any signal. One might trap all signals which stop or terminate the process, in order to reset the terminal before relinquishing it, but that's a major hassle and there's always SIGKILL and SIGSTOP. I think the best way to do it in BSD-derived systems is to use the asynchronous i/o facilities of fnctl(): res = fcntl(fd, F_SETFL, FASYNC); This will cause a SIGIO to be delivered to the calling process when input is available on descriptor fd. This is better than simply polling a nonblocking descriptor for two reasons: it is truly asynchronous, and it doesn't leave the terminal in a funky state if the calling process dies unexpectedly. You're still not completely out of the woods, however, as terminal input which triggers a SIGIO may still disappear if erased, and a subsequent read() would then block. You can also have an arbitrarily large amount of input available, not just one character. To handle this, I wrap the actual read()s in my code with a nonblocking section: res = fcntl(fd, F_SETFL, FNDELAY); while ((newly_read = read(fd, buf, buf_size)) > 0) { /* process newly_read characters */ } res = fcntl(fd, F_SETFL, FASYNC); You still run a risk of being interrupted or terminated while in nonblocking mode, but the risk is much reduced in that very little time is spent in this section of code. Further, the set of signals which can interrupt or terminate the process can be reduced to externally generated signals through proper debugging of the code in the nonblocking section. Thus, a SIGFPE, SIGBUS, or SIGSEGV generated elsewhere in your application won't blow away the parent shell anymore. One might also use select(2) to avoid blocking on the terminal read()s; I haven't yet tried this, and probably never will as I'm coming to the conclusion that diddling with the terminal from within an application is a Bad Idea in general. I think the Right Idea is to set up the terminal editor as a separate process, and pipe its output to the application. The application can do what it wants with the pipe - catch SIGIOs on it, set it nonblocking, whatever - without messing up the terminal state. The terminal editor can operate in a more sane mode, like CBREAK, and is better able to anticipate what signals it might need to catch to restore the terminal's proper state before exiting. This scheme also isolates the (very system-dependent) terminal handling code in its own process, making porting easier, and it would further reduce somewhat the postings to comp.unix.whatever and comp.lang.c asking ``How can I emulate kbhit() and getch()?'' Jim Vlcek (vlcek@caf.mit.edu vlcek@athena.mit.edu uunet!mit-caf!vlcek)