Skip to content

Commit

Permalink
exec: Don't exit after error in interactive shells (#803)
Browse files Browse the repository at this point in the history
As of POSIX.1-2024, interactive shells are not allowed to exit when
exec(1) fails to run a command[*]. This commit amends ksh's
implementation to comply with this requirement (in path_exec()).

[*]: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_21
  • Loading branch information
JohnoKing authored Dec 18, 2024
1 parent e67af95 commit 53937b9
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 3 deletions.
5 changes: 5 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ This documents significant changes in the dev branch of ksh 93u+m.
For full details, see the git log at: https://github.com/ksh93/ksh
Uppercase BUG_* IDs are shell bug IDs as used by the Modernish shell library.

2024-12-18:

- Persuant to POSIX.1-2024, exec(1) will no longer cause the shell to exit
when it fails to replace an interactive shell with the given command.

2024-12-08:

- [v1.1] Fixed a bug in the test and [ built-in commands where superfluous
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/ksh93/include/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#include <ast_release.h>
#include "git.h"

#define SH_RELEASE_DATE "2024-12-08" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_DATE "2024-12-18" /* must be in this format for $((.sh.version)) */
/*
* This comment keeps SH_RELEASE_DATE a few lines away from SH_RELEASE_SVER to avoid
* merge conflicts when cherry-picking dev branch commits onto a release branch.
Expand Down
16 changes: 14 additions & 2 deletions src/cmd/ksh93/sh/path.c
Original file line number Diff line number Diff line change
Expand Up @@ -1001,8 +1001,20 @@ noreturn void path_exec(const char *arg0,char *argv[],struct argnod *local)
pp = path_nextcomp(pp,arg0,0);
}
while(pp);
/* force an exit */
((struct checkpt*)sh.jmplist)->mode = SH_JMPEXIT;
if(sh_isstate(SH_EXEC) && sh_isstate(SH_INTERACTIVE))
{
/*
* An error just occurred and the shell cannot exit because it's
* interactive. Reincrement SHLVL and turn off the SH_EXEC state.
*/
sh.shlvl++;
sh_offstate(SH_EXEC);
}
else
{
/* Force an exit */
((struct checkpt*)sh.jmplist)->mode = SH_JMPEXIT;
}
errno = not_executable ? not_executable : sh.path_err;
switch(errno)
{
Expand Down
12 changes: 12 additions & 0 deletions src/cmd/ksh93/tests/path.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1037,5 +1037,17 @@ got=${ type -t whence_t_test 2>&1; }
(((e = $?) > 1)) && err_exit 'getconf builtin fails when on same path as external getconf' \
"(got status $e$( ((e>128)) && print -n /SIG && kill -l "$e"))"

# ======
# POSIX.1-2024: If the exec command fails ... an interactive shell may exit from a
# subshell environment but shall not exit if the current shell environment
# is not a subshell environment.
# https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_21
if((!SHOPT_SCRIPTONLY));then
exp=0
output=$($SHELL -ic $'PATH=/dev/null exec notacommand\nexit 0' 2>&1)
got=$?
((got==exp)) || err_exit "interactive shells exit after exec(1) fails to run a command (expected status '$exp', got status '$got' with output $(printf %q "$output"))"
fi # !SHOPT_SCRIPTONLY

# ======
exit $((Errors<125?Errors:125))

0 comments on commit 53937b9

Please sign in to comment.