Subject: on tcsh and the abberant random exit...
To: None <tech-kern@netbsd.org>
From: Andrew Brown <atatat@atatdot.net>
List: tech-kern
Date: 08/13/2001 13:03:16
first, a little background. there's a weird condition that tcsh
experiences when on the last line of an xterm and you try to
background a command that doesn't exist (ie, you type garbage or just
misspell something). tcsh receives the SIGCHLD from the failed child
exiting at the same time that it's in the middle of a call to
tcsetattr() and errno is set to ENOTTY, which strikes me as wrong.
the ENOTTY errno comes from tty_pty.c:ptyioctl(). the surrounding
code looks like this:
error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p);
if (error < 0)
error = ttioctl(tp, cmd, data, flag, p);
if (error < 0) {
if (pti->pt_flags & PF_UCNTL &&
(cmd & ~0xff) == UIOCCMD(0)) {
if (cmd & 0xff) {
pti->pt_ucntl = (u_char)cmd;
ptcwakeup(tp, FREAD);
}
return (0);
}
printf("ka-pwing!\n");
error = ENOTTY;
}
note that i added the ka-pwing and it gets printed each time tcsh does
the weird bugaboo.
now to analyze backwards.
tp->t_linesw->l_ioctl points to nullioctl(), and that always returns
-1. that means that ttioctl() will always get called.
ttioctl() is in tty.c and is basically a large lump of switches.
TIOCSETAW (from the tcsetattr() call) is handled first by this (which
blocks the process until it's in the foreground):
while (isbackground(curproc, tp) &&
p->p_pgrp->pg_jobc && (p->p_flag & P_PPWAIT) == 0 &&
!sigismasked(p, SIGTTOU)) {
pgsignal(p->p_pgrp, SIGTTOU, 1);
* error = ttysleep(tp, &lbolt, TTOPRI | PCATCH, ttybg, 0);
if (error)
return (error);
}
break;
which is one possible place for an error to pop up. then later, after
we're "not in the background", it starts doing the ioctl work and does
this:
s = spltty();
if (cmd == TIOCSETAW || cmd == TIOCSETAF) {
* if ((error = ttywait(tp)) != 0) {
splx(s);
return (error);
}
if (cmd == TIOCSETAF)
ttyflush(tp, FREAD);
}
if (!ISSET(t->c_cflag, CIGNORE)) {
/*
* Set device hardware.
*/
* if (tp->t_param && (error = (*tp->t_param)(tp, t))) {
splx(s);
return (error);
} else {
...
and the rest just chugs through and never checks or sets error.
presumably for a pseudo tty, since there's no underlying hardware, the
tp->t_param call is skipped. that means that ttywait() must be the
problem. ttywait() possibly gets an error from calling ttysleep()
(which we last saw in the background wait code in ttioctl(), so now we
can burn two bridges with one stone), and ttysleep() just calls
tsleep() (which is actually macroed to ltsleep(). additionally, if
tsleep() doesn't return an error, ttysleep() specifically returns 0 or
ERESTART.
ltsleep() in kern_synch.c has five places it returns:
return (0);
return (EWOULDBLOCK);
return (EINTR);
return (ERESTART);
return (0);
each of which ought to be handed back up to ttioctl() ultimately. the
problem is, it seems to me, that ERESTART is -1, which some things
don't restart. of course, being told to restart something and then
returning ENOTTY isn't terribly useful, is it? (it also seems weird
to me that ERESTART is -1 which isn't terribly distinguishable from an
error).
i think that the return value of ENOTTY should be changed to EINTR.
does anyone agree? disagree? care? have comments?
--
|-----< "CODE WARRIOR" >-----|
codewarrior@daemon.org * "ah! i see you have the internet
twofsonet@graffiti.com (Andrew Brown) that goes *ping*!"
andrew@crossbar.com * "information is power -- share the wealth."