Trigger ERR trap on permanent I/O redirection failure
authorkn <kn@openbsd.org>
Mon, 10 Oct 2022 14:57:48 +0000 (14:57 +0000)
committerkn <kn@openbsd.org>
Mon, 10 Oct 2022 14:57:48 +0000 (14:57 +0000)
commitf8c806b8e6f01d12a654a7bf412d3ed5ef82af70
treeab10e9c1a79b0bbeb715533ad585d5c18ab5c807
parent80203c1b92b73aecbf6d1a78cb8b0cf552b96653
Trigger ERR trap on permanent I/O redirection failure

The following three cases behave identical in bash(1), but our ksh
(ksh93 also) fails to run the trap in the last case:

(non-zero exit code is trigger, no redirection)
$ ksh -c 'trap "echo ERR" ERR ; false'
ERR

(failed redirection is trigger, 'echo' was not executed)
$ ksh -c 'trap "echo ERR" ERR ; echo >/'
ksh: cannot create /: Is a directory
ERR

(failed redirection, no execution, trap was NOT triggered)
$ ksh -c 'trap "echo ERR" ERR ; exec >/'
ksh: cannot create /: Is a directory

bash(1) prints "ERR" in all three cases, as expected.
ksh93 behaves like our ksh(1).

In ksh `exec' is a builtin (CSHELL), but also special (SPEC_BI):
$ type alias
alias is a shell builtin
$ type exec
exec is a special shell builtin

Without command and redirection alone, `exec' permanently redirects I/O for
the shell itself, not executing anything;  it is the only (special) builtin
with such a special use-case, implemented as c_sh.c:c_exec().

This corner-case is overlooked in exec.c:execute() which handles iosetup()
failure for all commands, incl. builtins.

Exclude c_exec() from the rest of special builtins to ensure it runs the
ERR trap as expected:

$ ./obj/ksh -c 'trap "echo ERR" ERR ; exec >/'
ksh: cannot create /: Is a directory
ERR

Also add three new regress cases covering this;  rest keep passing.

OK millert
bin/ksh/exec.c
regress/bin/ksh/trap.t [new file with mode: 0644]