tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Forthcoming shell (/bin/sh) changes

I have a fix for PR bin/53550  wrong exit status of a command that is
(more or less) just a here-doc which contains a command substitution...

Yes, I know, no-one cares ...   that fix is done, but is waiting for
me to get up the energy to add some ATF tests for it (& perhaps various
similar related issues).

More importantly here, and completely unrelated to the previous issue,
I am planning to change the way traps (particularly interrupt traps)
work in interactive shells (plus one more trap related change).

First, in an interactive shell, a caught SIGINT will get the trap
executed, and then another prompt issued, if the SIGINT happens while
waiting to read the next command from the terminal.   Currently, the
trap does not fire until the user enters a \n to complete the current
command line (often an empty command line).

You can see the difference if you (using sh) do
	xxxx ^C
with SIGINT not trapped (you'll get a new prompt immediately).

On the other hand, if you do
	trap 'echo interrupt' INT
first, and then repeat the

	xxxx ^C

you're likely to see no response at all, until
you later type another ^C (wondering if you actually
typed the first...) which will give a new prompt.

In either case, when you (after this) type a \n the
trap (the "echo interrupted") will fire, and "interrupted"
will appear on the terminal.   If you waited a while between
the ^C and the \n, and forget the ^C was ever entered, you
might even wonder what the message is about, as nothing was
interrupted anytime particularly near when it appears.

Caution: if you do end up typing just one ^C (and assuming
you have command line editing enabled - but almost everyone
does) then the "xxxx" that had been typed will still be
executed as a command (a second ^C will abort that).

There are reasons for all this odd behaviour in the way that
traps are handled in the shell, but I don't really believe
that any of them are particularly good ones.   That is, I doubt
any of it is intentional, it all just happens because of the
way all this is imlemented.

Second, in (some) other shells (but not all), if you are
executing a complex command, eg:

	for n in 1 2 3
		sleep 10

(in the foreground) and you interrupt (^C) (and SIGINT is being
trapped) then the current action (shared with other shells - this
is ancient behaviour) is for the interrupt to kill the running "sleep"
(that's normal, and required), then for the trap to fire (with the
trap command as above, "interrupted" will appear), and then the loop
will continue to run, starting the next sleep.   In this example,
three ^C's (one during each sleep) end the loop quickly.

That's exactly what should happen for a non-interactive shell (for
any trap) but for SIGINT in particular, in an interactive shell,
IMO the better behaviour (more intuitive) is for a trapped SIGINT
to act just like an untrapped one (except to also run the trap action
command of course) and abort the loop (any complex command) completely.

So, unless there are (reasoned) objections, that is what I am planning
to make happen.   It will probably not affect almost anyone, as setting
traps in interactive shells is not a common thing to do.

Note: this looks like it might be related to PR bin/50431 ("sh job control
doesn't work for loops") - but it isn't.   That is a much more difficult
problem, which won't be attacked before NetBSD 9.   I thought I had found
a relatively easy to implement fix for that, but the idea was nonsense,
(I mean, complete unbelievable trash) so I abandoned that one.   Before
I went off the rails on that, I had conceived the basis of a different
fix for that PR, but that would be a major operation, which, while it
shouldn't affect any normal use of the shell (when you're not attempting
to use job control on loops etc) it might.   It will also take time to
code properly.

The changes above really only apply to SIGINT and are restricted to
that (SIGINT is handled differently in the shell to all other signals
already - though not when it is trapped, that is the root cause of the
weird differences in behaviour).   Also note that the two changes are
not completely independent.

I could be convinced to make SIGQUIT act the same, if people think that
is worth it - SIGQUIT isn't normally used very much, except when you're
running a command and want to force it to dump core (that works now,
and won't change) - an interactive shell largely just ignores (but not
in the SIG_IGN sense) SIGQUIT if it is not trapped.   If it is, it acts
like any other trapped signal (that is, its behaviour, when trapped,
is just like reported above for SIGINT when trapped, as it currently is.)

The unrelated change (just because I was looking at traps) is to alter
a long (very long) time behaviour related to traps and SIGKILL and SIGSTOP.

We know (I hope) that those signals are magic - no process can do anything
to alter their standard behaviour in any way at all, and the shell is not
exempt from that.   But the shell pretends to not know that, and allows
users to pretend to catch or ignore those signals via the trap command.
So do most (but not all) other shells (in POSIX attempting to do anything
to those two signals produces undefined behaviour).

I can't imagine a reason anything would ever even attempt to trap or ignore
either of those signals (I've never seen anything do that .. other than
for testing) but I am proposing to issue an error instead of simply accepting
the command, pretending it worked, but otherwise ignoring it.   That is
have the trap command exit with status 1 instead of 0, and issue an error
message (except in SMALL shells ... just because the code to generate a
nicely appropriate message is more than I want to add to a SMALL shell, it
could have a much simpler message if anyone really believes that anyone
would ever see it!)   Along with that, the trap command with no args would
no longer ever list those signals (as it only outputs anything for traps
not in their default state, and these would no longer be able to ever
depart that state after the change).   But "trap -p" (which POSIX is
planning to copy from us, and include in Issue 8) lists all signals
(POSIX are not planning to require these two to be included however)
so I am planning to not include them there (any more) either.   Including
them just makes more output, for no benefit...    However, "trap -p KILL"
will still work (but is guaranteed to generate "trap -- - KILL" (same for
STOP) and those commands if executed (that is, explicitly setting SIGKILL
or SIGSTOP "back" to the default state) will continue to work without error.

Any opinions on that one?

Lastly, a change I am not making (now) but perhaps might some day.

There has been a big argument (well, between me, and one other list
member) about what the standard requires as output from the "trap"
command for signals that are ignored on entry to a non-interactive
shell, but which the script has attempted to catch (or set back to,
effectively, SIG_DFL).   It is clear that the trap cannot work, if
the signal is ignored at entry, then it should stay ignored throughout
the life of the shell - the reasons for this relate mostly to terminal
generated signals and they way the terminal sent such signals in the
days before job control and process groups.   Yet all shells (including
us) make that happen - or at least pretend to - to the script.

However, ash based shells (which includes our shell of course) allow
the script to set an action for such signals - and just never arrange
to catch the signal to make the trap work, or to set the signal back
to SIG_DFL if that is what the trap command requested).   In this ash
copied the original 7th edition Bourne shell, which acted this way too
(though its output format was useless for anything, that ash also
originally copied, but that was fixed ages ago.)

There is some opinion that this is incorrect (according to POSIX),
and than any attempt to set a trap on a signal which was ignored at
the startup of a non-interactive shell should simply be ignored (have
no effect at all ... generating an error is clearly not required, and
probably should be forbidden - no-one does that, and doing so might
break scripts).   Shells that act this way (mostly) don't report such
signals in the output of the (no arg) trap command.   The one exception
is modern versions of bash (it changed sometime during the bash4 era I
believe) which reports such signals as ignored in "trap" output.   There
was some opinion (not from the current, and long time bash maintainer
incidentally) that POSIX actually requires this bash behaviour (despite
no shells at the time the relevant text was written - none - implementing
that).    I think (hope) that opinion has now been debunked...

But whether or not we are actually permitted to accept an attempt to
alter the trap for one of those signals, and in the trap command, report
its state, as if it worked, is less clear.   The "reference implementation"
shells from when the standard was created did not do that (they simply
ignore the trap command that attempts to alter the state, and emit nothing
related to such signals in the trap (without args) command).

Just in case, are there any opinions from people here about what is the
best behaviour here?   If people think it is worthwhile, we could change
now, we would certainly not be moving away from POSIX by doing that, and
might be moving closer.

What we should (according to the standard) report for "trap -p" or
"trap -p SIGxxx" for such a signal) is not yet set in stone, but I
doubt that there is likely to be much difference (indicentally, the
-p arg to trap, with signal name args as well was invented in ksh93
I believe, and copied, with the same purpose, but different output format,
in bash ... what NetBSD added was a meaning for "trap -p" without args
in order to fix a problem with the "trap" command without args, which
cannot reasonably be fixed there - it is too much of a difference for
something so old and set in so much concrete).   Both forms of the -p
option are being added to the standard - with signal arg(s) which
copies the bash output format (which is the same as the no-arg format
which POSIX specifies) rather than the ksh93 format (which is arguably
easier to process) -- we copied bash's format as well.   Without signal
args the -p option is just like trap with no args at all - except that
the output is required to include all possible signals, except KILL and
STOP as an option .. if they are included it places constraints upon other
aspects of the trap command (which is why I am planning on no longer
including them .. not that it really makes a lot of difference.)

OK, enough for now, I am still working on verifying the trap changes
I am currently proposing making (making sure they work correctly, all
the time) and of course, there is plenty of opportunity to make
different (or even no, though I really hope we don't do that) changes
if that's the general opnion.


Home | Main Index | Thread Index | Old Index