**Overview:**
Wine currently reports an exit code of zero for processes that terminate due to either of the following conditions:
- The Linux process that represents the Windows process receives a signal such as `SIGKILL` (e.g. due to a user manually killing the process, or the Linux OOM killer targeting the process due to memory pressure)
- The process fails to start because the image is a 32-bit executable and Wine has only been built with 64-bit support
Both of these scenarios represent failures, and so a non-zero exit code is appropriate. This patch ensures that an exit code of 1 is reported for these abnormal process termination edge cases.
**Underlying cause:**
The following logic flow occurs in the Wine server for typical process termination:
1. When a `process` object is created by the Wine server, its `exit_code` field is [set to an initial value](https://gitlab.winehq.org/wine/wine/-/blob/wine-8.16/server/process.c?ref_ty...) of `STILL_ACTIVE`, and the `exit_code` field in each of its corresponding `thread` objects is [set to a default value of zero](https://gitlab.winehq.org/wine/wine/-/blob/wine-8.16/server/thread.c?ref_typ...).
2. When a client process terminates, it sends a `terminate_process` protocol request to the server, and the handler [passes the specified exit code](https://gitlab.winehq.org/wine/wine/-/blob/wine-8.16/server/process.c?ref_ty...) to the `terminate_process()` function. This function sets the `is_terminating` field of the process object [to a value of 1](https://gitlab.winehq.org/wine/wine/-/blob/wine-8.16/server/process.c?ref_ty...), and if the exit code is non-zero then it also [copies its value to each of the thread objects](https://gitlab.winehq.org/wine/wine/-/blob/wine-8.16/server/process.c?ref_ty...) for the process.
3. The client process closes the pipe that it had been using to communicate with the server.
4. The server [detects the pipe close event](https://gitlab.winehq.org/wine/wine/-/blob/wine-8.16/server/process.c?ref_ty...) and calls the `kill_process()` function, specifying a value of zero for the `violent_death` parameter.
5. The logic in the `kill_process()` function identifies this as a [normal termination on pipe close](https://gitlab.winehq.org/wine/wine/-/blob/wine-8.16/server/process.c?ref_ty...), and then subsequently calls the `kill_thread()` function to [kill each of the threads for the process](https://gitlab.winehq.org/wine/wine/-/blob/wine-8.16/server/process.c?ref_ty...).
6. The `kill_thread()` function [then calls](https://gitlab.winehq.org/wine/wine/-/blob/wine-8.16/server/thread.c?ref_typ...) `remove_process_thread()`, which [copies the exit code from the last thread object](https://gitlab.winehq.org/wine/wine/-/blob/wine-8.16/server/process.c?ref_ty...) to the `exit_code` field of the process object.
7. The final value of the `exit_code` field is [reported when responding](https://gitlab.winehq.org/wine/wine/-/blob/wine-8.16/server/process.c?ref_ty...) to `get_process_info` protocol requests, [as sent by](https://gitlab.winehq.org/wine/wine/-/blob/wine-8.16/dlls/ntdll/unix/process...) `NtQueryInformationProcess()`.
The abnormal process termination scenarios listed earlier will skip the `terminate_process` protocol request and immediately close the pipe. As a result, the `is_terminating` field of the process object will retain its default value of zero, and the `exit_code` field of each thread for the process will also remain at the default value of zero.
Since the logic in `process_poll_event()` always treats a pipe close event as a non-violent termination, `remove_process_thread()` will simply copy the exit code value of zero from the last thread into the process object, and this will then be reported when querying the process exit code.
**Fix details:**
The fix modifies `process_poll_event()` to check the value of the `is_terminating` field of the process object, and to treat a pipe close event as a violent termination if the value of the field is zero. This triggers the alternative code path in `kill_process()` that calls `terminate_process()` and [specifies an exit code value of 1](https://gitlab.winehq.org/wine/wine/-/blob/wine-8.16/server/process.c?ref_ty...), which is copied to the thread objects and subsequently to the process object.
In my testing, only the abnormal process termination scenarios listed above result in the value of the `is_terminating` field being zero when the pipe is closed. In all other scenarios, I have observed the value to be 1 when the pipe close event is detected. Non-abnormal scenarios tested include ordinary process completion with both zero and non-zero exit codes, crashes in Windows processes due to unhandled exceptions, and ending processes via the Wine implementation of Task Manager.
-- v4: wineserver: Report non-zero exit code for abnormal process termination