Subject: Re: bin/10879: su does not reset terminal settings after Ctrl-C
To: None <gnats-bugs@gnats.netbsd.org, netbsd-bugs@netbsd.org,>
From: Greywolf <greywolf@starwolf.com>
List: netbsd-bugs
Date: 08/24/2000 10:12:15
On Thu, 24 Aug 2000, Greg A. Woods wrote:

[okay, so maybe I agreed too soon! :-)]

# [ On Thursday, August 24, 2000 at 00:46:04 (-0400), James Chacon wrote: ]
# > Subject: Re: bin/10879: su does not reset terminal settings after Ctrl-C
# >
# > I think one of the follwing need to occur:
# > 
# > 1. Block all signals in getpass so everything is consistant across the board
# >    and document that code using getpass(3) needs to expect the non-obvious
# >    SIGINT + return == exit behaviour.

This is the easiest case to program, but it's not correct.

# > 2. Put signal handlers in getpass(3) which catch everything, reset the 
# >    terminal settings and then pass off to either the default or a previously
# >    installed handler. (YUCK)

This is easy to program but shouldn't be left in a library routine.

# > 3. Don't block anything and let the terminal/shell do their thing on exit.
# >    Expect the application code to catch relevant signals and possibly have
# >    to deal with resetting echo mode on the terminal. (i.e. updating the
# >    man page to getpass(3) to include "If aborted by a signal the terminal will
# >    be left in non-echo mode."

This looks like a reasonable default and seems to be what I remember as well.

# > 
# > 4. Ignore all the issues related around this and just fix su to use it's own
# >    getpass implementation and catch the signals itself. (Still update 
# >    the getpass(3) man page at a minimum to explain what's occuring here).

This would be silly as it would encourage other programs to use THEIR own
getpass() routines, bypassing the system implementation altogether, which
would leave us with a potentially decrepit (by comparison) getpass().

# > I actually think #3 is the most reasonable but it will require any userland
# > code relying on getpass to accomodate change. (24 files today)

Not bad.

# You missed another posible solution....
# 
#   5. have getpass(3) put the terminal in raw mode and emulate all of the
#      character processing within itself.

That is, as you note, tricky to get right.

# BTW #4 is completely the wrong approach and probably shouldn't even be
# listed as a potential solution.
# 
# #2 is possible, but a little yucky as you say.  It's also probably about
# as hard to get right as my #5 too.

No, #5 is harder.  The signal handling code would add about 3 lines per
signal, I think.

# In reality though #3 is the only correct solution, and is the one that's
# been chosen by Unix for a long time now (I'm not sure since exactly
# when, though I do note from the sources I have that up to System III
# that SIGINT is simply ignored until after a '\n' or EOF is returned and
# that only echo processing is modifed, however all of the AT&T Unix
# manuals I have, including the System V Release 2.2 manual page from
# about 1986, state explicitly that "An interrupt will terminate input and
# send an interrupt signal to the calling program before returning." with
# the implication being that echoing is not re-enabled in that case).

I don't remember sigint being ignored at all for a long time!

# Of course users of getpass() must be fixed up to catch SIGINT and to
# properly reset the terminal modes when it is triggered....  We can't be
# relying on interactive shells to restore the terminal modes!

No, but if the shell happens to do it, so much the better :-).  And
more of them do this these days.

# I can assure you that I was very happy with the behaviour of System V
# 'su' w.r.t. interrupts during the getpass() call, and I've been rather
# disappointed by the return of the broken behaviour in even modern BSDs.

To ignore SIGINT is broken, IMHO.  Why was this done?

Shouldn't the signal(SIGINT, SIG_IGN)/signal(SIGINT, SIG_DFL) pair
nestle around the getpass() call?

# My remaining problem with *BSD tty behaviour, which is demonstrated by
# getpass(), is the mis-handling of EOF.  Traditionally I recall ^D
# working at any point on a line.

It *does* work at any point in a line.  But if you type anything BEFORE
the EOF, you have to hit it twice.  The second EOF will cause everything
typed before the FIRST EOF to be sent.

It just doesn't work the way you want/expect it to work!

# In BSD it only has the effect of
# generating EOF when entered at the beginning of a line.  This frustrates
# me almost every time I want to abort 'su' as I have to remember that: a)
# BSD's version still blocks SIGINT; and b) that EOF only works at the
# beginning of the line so now I have to use ^U^D instead.

Just hit two EOFs in a row and it'll abort (or if you do "CorrectPasswd^D^D"
it will succeed).

[tangent:  EOF was never meant as an abort char; it's an indication that
 input is finished, i.e. "I'm done, take what I gave you."

 Of course it doesn't help that 'sh' does the following:

  %
  $ ls^D^D
  [output from ls]
  $ %

  and 'csh' does the following

  % ls^D^Dexit

  Hmmm.  Is this broken?  I don't know; I always considered the older tty
  driver from AT&T to be broken (backspacing over the prompt?  No word
  erase, no nice line kill and no job control.  Gotta love it :/]
 
				--*greywolf;
--
BSD: Mach 3 stealthOS, undetectable by media radar.