From 790e37727319c3dd9c2d4e45dac9b6cc38a5d25f Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sat, 22 Jun 2024 10:40:16 +0100 Subject: [PATCH] win32: revert 'don't set error mode' Commit eb376b5d1 (win32: don't set error mode) removed a call to SetErrorMode(SEM_FAILCRITICALERRORS). But the documentation says: Best practice is that all applications call the process-wide SetErrorMode function with a parameter of SEM_FAILCRITICALERRORS at startup. This is to prevent error mode dialogs from hanging the application. Doing this prevents the system from displaying useful information, though. The application should attempt to tell the user what went wrong. Reinstate the call to SetErrorMode() and try to provide an error message, at least for the situation mentioned in issue #423 and other similar cases. Adds 360-368 bytes. (GitHub issue #423) --- libbb/appletlib.c | 4 ++ win32/process.c | 93 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 68 insertions(+), 29 deletions(-) diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 51824f1b39..c90285ae90 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -1349,6 +1349,10 @@ int main(int argc UNUSED_PARAM, char **argv) break; } } + + /* Have this process handle critical errors itself: the default + * system-generated error dialogs may be inconvenient. */ + SetErrorMode(SEM_FAILCRITICALERRORS); #endif #if defined(__MINGW64_VERSION_MAJOR) diff --git a/win32/process.c b/win32/process.c index caea87492d..94a1e23a99 100644 --- a/win32/process.c +++ b/win32/process.c @@ -443,7 +443,65 @@ BOOL WINAPI kill_child_ctrl_handler(DWORD dwCtrlType) return FALSE; } -static NORETURN void wait_for_child(HANDLE child) +static int exit_code_to_wait_status_cmd(DWORD exit_code, const char *cmd) +{ + int sig, status; + DECLARE_PROC_ADDR(ULONG, RtlNtStatusToDosError, NTSTATUS); + DWORD flags, code; + char *msg = NULL; + + if (exit_code == 0xc0000005) + return SIGSEGV; + else if (exit_code == 0xc000013a) + return SIGINT; + + // When a process is terminated as if by a signal the Windows + // exit code is zero apart from the signal in its topmost byte. + // This is a busybox-w32 convention. + sig = exit_code >> 24; + if (sig != 0 && exit_code == sig << 24 && is_valid_signal(sig)) + return sig; + + // The exit code may be an NTSTATUS code. Try to obtain a + // descriptive message for it. + if (exit_code > 0xff) { + flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM; + if (INIT_PROC_ADDR(ntdll.dll, RtlNtStatusToDosError)) { + code = RtlNtStatusToDosError(exit_code); + if (FormatMessage(flags, NULL, code, 0, (char *)&msg, 0, NULL)) { + char *cr = strrchr(msg, '\r'); + if (cr) { // Replace CRLF with a space + cr[0] = ' '; + cr[1] = '\0'; + } + } + } + + if (cmd) + bb_error_msg("%s: %sError 0x%lx", cmd, msg ?: "", exit_code); + else + bb_error_msg("%s: %sError 0x%lx" + 4, msg ?: "", exit_code); + LocalFree(msg); + } + + // Use least significant byte as exit code, but not if it's zero + // and the Windows exit code as a whole is non-zero. + status = exit_code & 0xff; + if (exit_code != 0 && status == 0) + status = 255; + return status << 8; +} + +static int exit_code_to_posix_cmd(DWORD exit_code, const char *cmd) +{ + int status = exit_code_to_wait_status_cmd(exit_code, cmd); + + if (WIFSIGNALED(status)) + return 128 + WTERMSIG(status); + return WEXITSTATUS(status); +} + +static NORETURN void wait_for_child(HANDLE child, const char *cmd) { DWORD code; @@ -451,7 +509,7 @@ static NORETURN void wait_for_child(HANDLE child) SetConsoleCtrlHandler(kill_child_ctrl_handler, TRUE); WaitForSingleObject(child, INFINITE); GetExitCodeProcess(child, &code); - exit(exit_code_to_posix(code)); + exit(exit_code_to_posix_cmd(code, cmd)); } int @@ -459,7 +517,7 @@ mingw_execvp(const char *cmd, char *const *argv) { intptr_t ret = mingw_spawnvp(P_NOWAIT, cmd, argv); if (ret != -1) - wait_for_child((HANDLE)ret); + wait_for_child((HANDLE)ret, cmd); return ret; } @@ -468,7 +526,7 @@ mingw_execve(const char *cmd, char *const *argv, char *const *envp) { intptr_t ret = mingw_spawn_interpreter(P_NOWAIT, cmd, argv, envp, 0); if (ret != -1) - wait_for_child((HANDLE)ret); + wait_for_child((HANDLE)ret, cmd); return ret; } @@ -896,33 +954,10 @@ int FAST_FUNC is_valid_signal(int number) int exit_code_to_wait_status(DWORD exit_code) { - int sig, status; - - if (exit_code == 0xc0000005) - return SIGSEGV; - else if (exit_code == 0xc000013a) - return SIGINT; - - // When a process is terminated as if by a signal the Windows - // exit code is zero apart from the signal in its topmost byte. - // This is a busybox-w32 convention. - sig = exit_code >> 24; - if (sig != 0 && exit_code == sig << 24 && is_valid_signal(sig)) - return sig; - - // Use least significant byte as exit code, but not if it's zero - // and the Windows exit code as a whole is non-zero. - status = exit_code & 0xff; - if (exit_code != 0 && status == 0) - status = 255; - return status << 8; + return exit_code_to_wait_status_cmd(exit_code, NULL); } int exit_code_to_posix(DWORD exit_code) { - int status = exit_code_to_wait_status(exit_code); - - if (WIFSIGNALED(status)) - return 128 + WTERMSIG(status); - return WEXITSTATUS(status); + return exit_code_to_posix_cmd(exit_code, NULL); }