Strange SUN behaviour.

Jim Vlcek vlcek at mit-caf.MIT.EDU
Mon Oct 9 07:16:26 AEST 1989


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 at caf.mit.edu  vlcek at athena.mit.edu  uunet!mit-caf!vlcek)



More information about the Comp.unix.wizards mailing list