fix: a log file that cannot be opened no longer closes stderr #558

Open
zeldaootr wants to merge 3 commits from zeldaootr/Longshot:log-open-failure-keeps-stderr into main
Contributor

Type: fix · Risk: low · Tested on: Linux x86-64 (game-free); Steam Deck OLED, SteamOS 3.9.5 (a tester's run) · Needs: none

Why

When the game cannot write logs/longshot_log.txt (a logs/ folder left by a run as root, a read-only game folder), it lost its error output altogether, and the next file it opened took the error output's place, so stray messages could be written into that file. The tracker has no issue for it (searched).

How to reproduce

Seen on: Steam Deck OLED, SteamOS 3.9.5, main 7ff5256e (a tester's run); the game-free test reproduces it on Linux
Settings and switches: logs/ not writable (chmod a-w logs); stderr neither a terminal nor a file (a socket, as a desktop launcher gives it), since only then does the game open longshot_log.txt
Steps: start the game through systemd-cat, so stderr is the journal's socket, and read the journal
Expected: with the log file unopenable, the start-up messages go to the stderr the game was started with
Actual: on main the messages went nowhere: 0 lines in the journal, and no file in the game folder, the data folder, /tmp or /dev/shm held them

What changed

The game now opens its log file before it points stderr at it, so a log that cannot be opened leaves stderr as the game was started with. Nothing changes for a log that can be written.

  • host: game_main() in host/hos_main.c opens the log with fopen first and only then reopens stderr on it with freopen. freopen closes stderr when its open fails, which freed descriptor 2 for the next file the game opened (the guest memory's memfd).
  • tests: tests/test_log_open_failure.py cuts that code out of hos_main.c, makes the log's path unopenable (a file named logs where the folder would be) and checks that descriptor 2 is still open; a second run checks the log is opened in logs/. tests/test_udmabuf_warning.py, which cuts the same code by its text, is updated to the new first line.

Measurements

Verdict: pass, the bug is gone. On a tester's Steam Deck OLED with logs/ unwritable and stderr on the journal's socket, main's start-up messages went nowhere (0 lines), and with this PR all 630 reached the journal; a start from a terminal, which never reaches this code, was the same on both. The game-free test fails on main and passes here.

Hardware: Steam Deck OLED, AMD Custom APU 0932 (RADV VANGOGH), 16 GB, Mesa 26.2.0-devel git-5c65d4fc31, SteamOS 3.9.5 (build 20261023.100), kernel 7.2.7-valve2 (dukenheimer's run, test request T-ZH5M); game-free: Linux x86-64 server, no GPU or game
Scene: the start of the game into the intro, in a copy of a test game folder with logs/ unwritable, XDG_DATA_HOME an empty test folder; started headless (HOS_HEADLESS=1) from a shell over SSH, closed through the control bridge once it ran
Method: where the start-up messages went (the terminal, the journal, any file), counted in lines, and the frame count over 5 s to show the game ran. One start of each build in each way: in a pseudo-terminal (script), and through systemd-cat (stderr the journal's socket). A behaviour check with an all-or-nothing outcome. Game-free: python3 -I -B tests/test_log_open_failure.py at 928f7444 and at b12c8ba4 (rebased onto main aa95d35f), with gcc 13.3 and with clang 23.1.5; before that, at 25a3a664 and 5a5c05c0 on main 53f96d78, and before the first rebase at 388c926f and 6fb80632, twice each, alternating
How to reproduce: build main 7ff5256e and the combined build (main 7ff5256e with #550 to #558 merged in order), both x86-64-v3 with the release play profile and LLVM 23.1.5; then the steps under How to reproduce; for the game-free check, run the test command at each commit

Before: main @ 7ff5256e After: this PR @ 6fb80632 (in the combined build)
started as a launcher starts it (stderr the journal's socket): start-up messages nowhere: 0 lines in the journal, no file held them the journal: all 630 lines
started from a terminal: start-up messages the terminal: 603 lines the terminal: 595 lines
log file none (both starts) none (both starts)
the game ran (frame count over 5 s: launcher start; terminal start) 226 to 552; 185 to 516 320 to 646; 227 to 553
[hos host] longshot unknown (source 15ee0b2f)
[hos host] run_id: 062ee4238a34f071aed4472c8c29cd92
[hos host] control: listening on <socket> (HOS_CONTROL: this user only, no TCP; guest memory off; docs/control.md)
[hos host] mem: the guest window is a memfd's (7), shared with the GPU as dma-bufs
[hos host] oe: output resolution 1280x720
[hos host] boot: image 96 MB at 0x80000000, 9809 MB of memory for the game
[hos host] boot: 14798 MB of memory, 8422 MB of swap
[hos host] vk: reserved core mask (fallback): 0xc0
[hos MainThread] vk: the Output resolution's sizes: 6 (the screen's modes, with the common sizes of their shapes, and the game's own), and one of the player's own; shapes 16:9, 32:9
[hos MainThread] vk: device 0: AMD Custom GPU 0932 (RADV VANGOGH), Vulkan 1.4, driver 0x6801063, type 1

The first 10 lines on the terminal with this PR, as the tester posted them (main's are the same apart from the source hash); with this PR the journal's lines start the same. The tester's report: #558 (comment)

Before: main @ aa95d35f (with the test, 928f7444) After: this PR @ b12c8ba4
stderr still open after the log could not be opened (gcc and clang the same) no yes
928f7444: FAIL: fcntl(2, F_GETFD) != -1 (stderr was closed: the next file the game opens takes descriptor 2) (exit 1, gcc and clang)
b12c8ba4: PASS: an unopenable log leaves the stderr the game was started with (exit 0, gcc and clang)

Flags and settings

None.

Testing

Tested: Linux x86-64, game-free. The new test fails on the test-only commit 928f7444 and passes at b12c8ba4, this branch rebased onto main aa95d35f, with gcc 13.3 and with clang 23.1.5 (before, it did the same at 25a3a664 and 5a5c05c0 on main 53f96d78, and at 388c926f and 6fb80632, twice each, alternating) (python3 -I -B tests/test_log_open_failure.py). On land/integration 27553c83 (main 7ff5256e with this PR and twelve other fixes merged), the thirteen fixes' own tests and the checks their fix commits edit pass, 16 of 16; the full python3 -I -B tests/run_all.py ran on that branch too (see the checklist).

In the game: dukenheimer ran test request T-ZH5M on a Steam Deck OLED (SteamOS 3.9.5) and reported a pass on 2026-11-15 at 03:06 UTC (#558 (comment)). The builds were the combined build of main 7ff5256e with #550 to #558 merged in order, this PR at its head then, 6fb80632 (b12c8ba4 after the rebases, the same change by git patch-id), and main 7ff5256e for the before; each in a copy of a test game folder with logs/ unwritable, started headless over SSH. Started as a launcher starts it (stderr the journal's socket), main's messages vanished and this PR's all reached the journal; started from a terminal, both builds showed them there (that start never opens the log, so it does not reach this change). The game ran past its start in all four. The tester did not report whether a stray message landed in another file on main (only that no file held the start-up messages).

The branch has been rebased since the tester's run, onto main aa95d35f, with its commits re-authored under the forge's noreply address; each commit's change is identical to before by git patch-id, so the commits named above are the ones tested and the result stands for this head (4ad3d404).

Not tested: Windows in the game, where the same lines run: the whole game was built and linked for Windows with llvm-mingw (see the checklist), but not run (the fixture checks POSIX descriptors and skips on Windows). A start through Steam in Game Mode with a window (the Deck's starts were headless, with the launcher's stderr made through systemd-cat). The causes named in Why (a logs/ left by a run as root, a read-only game folder): the tester made logs/ unwritable with chmod instead.

Must be tested: None

Risks

What could break: The log file is opened twice in a row (fopen, close, freopen); both truncate it, so nothing changes for a log that can be written. On Windows the same lines run; they are built and linked there, not yet run.

Turning it off: revert the commit

Compatibility: Nothing changes

Checklist

Everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description) of the status line's type.
  • Why gives the reason plainly and links the issues that fit (the tracker searched).
  • Measurements has a verdict, or one line saying why there is nothing to measure.
  • Testing names what was tested, what wasn't, and what must still be tested, where and how.
  • Rebased on current main. Onto main aa95d35f with no conflict, the commits re-authored under the forge's noreply address: each of the three commits' changes (the test, the fix, the docs/tested/ file) is identical to before by git patch-id. Since 53f96d78, main changed host/hos_main.c in #729 (9e6f231d), away from this PR's lines.
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named). Run on this branch at 4d8f3d76 on main 53f96d78, before this rebase (4ad3d404 now, the same patches by git patch-id; the Vulkan and SPIR-V headers from the CPM cache, 8 at once, on a loaded Linux x86-64 server): 531 passed, 15 failed, 48 skipped. The 15 are the ones that fail on main 53f96d78 in the same run: test_appimage_player_build, test_bake_portability, test_cpm_discovery, test_crash_dialog, test_env_on, test_lift_cache, test_lift_cleanup_error, test_make_game_cache, test_pipeline_tour, test_readback_late, test_vk_pgo, test_vram_budget_row, test_walk_key, nx2/test_mesh_first_types.py and nx2/test_texture_init_barrier.py. Main's run also timed out test_fpx_sticky (it passes alone there). After the rebase onto main aa95d35f, at 4ad3d404: 36 checks, this PR's test and those that read its changed files, with g++ 13.3 and with clang 23.1.5: 33 pass; the 3 that fail fail on main aa95d35f too (run alone there, both compilers): tests/test_crash_dialog.py (#626), tests/test_lift_cache.py (#628), tests/test_pipeline_tour.py (#627).
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

When it applies

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported. The game-free test twice each, alternating. The Steam Deck check is a behaviour check with an all-or-nothing outcome: one start of each build in each of the two ways.
  • A file in docs/tested/ for each new measured run (format in docs/tested-configurations.md). docs/tested/2026-11-15-log-open-failure-dukenheimer-t-zh5m.md, the tester's run; scripts/tested_configurations.py and tests/test_tested_configurations.py pass with it.
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. Linux: the game linked, in the tester's combined build (x86-64-v3, LLVM 23.1.5), and it ran. Windows: the whole game built from this branch's tip 4d8f3d76 (before this rebase; the same patches as 4ad3d404 by git patch-id) on Linux with RX_TARGET=windows and llvm-mingw 20261104 (nx2/make_game.py --no-prepare-shaders, tools.lock's Windows cpm hookshot-v2.1.0-dav1d-1.5.4, this server's CPU as the target, no wgpu-native): longshot.exe (431 MB) and longshot_vk.dll linked, not run. After the rebase onto main aa95d35f, host/hos_main.c at 4ad3d404 checked whole (-fsyntax-only -Wall -Wextra with the host's include paths) for Linux (clang 23.1.5) and Windows (llvm-mingw 20261104): the same warnings as main aa95d35f's, none new.
**Type:** fix · **Risk:** low · **Tested on:** Linux x86-64 (game-free); Steam Deck OLED, SteamOS 3.9.5 (a tester's run) · **Needs:** none ## Why When the game cannot write `logs/longshot_log.txt` (a `logs/` folder left by a run as root, a read-only game folder), it lost its error output altogether, and the next file it opened took the error output's place, so stray messages could be written into that file. The tracker has no issue for it (searched). ## How to reproduce **Seen on:** Steam Deck OLED, SteamOS 3.9.5, main 7ff5256e (a tester's run); the game-free test reproduces it on Linux **Settings and switches:** `logs/` not writable (`chmod a-w logs`); stderr neither a terminal nor a file (a socket, as a desktop launcher gives it), since only then does the game open `longshot_log.txt` **Steps:** start the game through `systemd-cat`, so stderr is the journal's socket, and read the journal **Expected:** with the log file unopenable, the start-up messages go to the stderr the game was started with **Actual:** on main the messages went nowhere: 0 lines in the journal, and no file in the game folder, the data folder, /tmp or /dev/shm held them ## What changed The game now opens its log file before it points stderr at it, so a log that cannot be opened leaves stderr as the game was started with. Nothing changes for a log that can be written. - host: `game_main()` in `host/hos_main.c` opens the log with `fopen` first and only then reopens stderr on it with `freopen`. `freopen` closes stderr when its open fails, which freed descriptor 2 for the next file the game opened (the guest memory's memfd). - tests: `tests/test_log_open_failure.py` cuts that code out of `hos_main.c`, makes the log's path unopenable (a file named `logs` where the folder would be) and checks that descriptor 2 is still open; a second run checks the log is opened in `logs/`. `tests/test_udmabuf_warning.py`, which cuts the same code by its text, is updated to the new first line. ## Measurements **Verdict:** pass, the bug is gone. On a tester's Steam Deck OLED with `logs/` unwritable and stderr on the journal's socket, main's start-up messages went nowhere (0 lines), and with this PR all 630 reached the journal; a start from a terminal, which never reaches this code, was the same on both. The game-free test fails on main and passes here. Hardware: Steam Deck OLED, AMD Custom APU 0932 (RADV VANGOGH), 16 GB, Mesa 26.2.0-devel git-5c65d4fc31, SteamOS 3.9.5 (build 20261023.100), kernel 7.2.7-valve2 (dukenheimer's run, test request T-ZH5M); game-free: Linux x86-64 server, no GPU or game Scene: the start of the game into the intro, in a copy of a test game folder with `logs/` unwritable, `XDG_DATA_HOME` an empty test folder; started headless (`HOS_HEADLESS=1`) from a shell over SSH, closed through the control bridge once it ran Method: where the start-up messages went (the terminal, the journal, any file), counted in lines, and the frame count over 5 s to show the game ran. One start of each build in each way: in a pseudo-terminal (`script`), and through `systemd-cat` (stderr the journal's socket). A behaviour check with an all-or-nothing outcome. Game-free: `python3 -I -B tests/test_log_open_failure.py` at 928f7444 and at b12c8ba4 (rebased onto main aa95d35f), with gcc 13.3 and with clang 23.1.5; before that, at 25a3a664 and 5a5c05c0 on main 53f96d78, and before the first rebase at 388c926f and 6fb80632, twice each, alternating How to reproduce: build main 7ff5256e and the combined build (main 7ff5256e with https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/550 to https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/558 merged in order), both x86-64-v3 with the release play profile and LLVM 23.1.5; then the steps under How to reproduce; for the game-free check, run the test command at each commit | | Before: main @ 7ff5256e | After: this PR @ 6fb80632 (in the combined build) | |---|---|---| | started as a launcher starts it (stderr the journal's socket): start-up messages | nowhere: 0 lines in the journal, no file held them | the journal: all 630 lines | | started from a terminal: start-up messages | the terminal: 603 lines | the terminal: 595 lines | | log file | none (both starts) | none (both starts) | | the game ran (frame count over 5 s: launcher start; terminal start) | 226 to 552; 185 to 516 | 320 to 646; 227 to 553 | ``` [hos host] longshot unknown (source 15ee0b2f) [hos host] run_id: 062ee4238a34f071aed4472c8c29cd92 [hos host] control: listening on <socket> (HOS_CONTROL: this user only, no TCP; guest memory off; docs/control.md) [hos host] mem: the guest window is a memfd's (7), shared with the GPU as dma-bufs [hos host] oe: output resolution 1280x720 [hos host] boot: image 96 MB at 0x80000000, 9809 MB of memory for the game [hos host] boot: 14798 MB of memory, 8422 MB of swap [hos host] vk: reserved core mask (fallback): 0xc0 [hos MainThread] vk: the Output resolution's sizes: 6 (the screen's modes, with the common sizes of their shapes, and the game's own), and one of the player's own; shapes 16:9, 32:9 [hos MainThread] vk: device 0: AMD Custom GPU 0932 (RADV VANGOGH), Vulkan 1.4, driver 0x6801063, type 1 ``` The first 10 lines on the terminal with this PR, as the tester posted them (main's are the same apart from the source hash); with this PR the journal's lines start the same. The tester's report: https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/558#issuecomment-22268 | | Before: main @ aa95d35f (with the test, 928f7444) | After: this PR @ b12c8ba4 | |---|---|---| | stderr still open after the log could not be opened (gcc and clang the same) | no | yes | ``` 928f7444: FAIL: fcntl(2, F_GETFD) != -1 (stderr was closed: the next file the game opens takes descriptor 2) (exit 1, gcc and clang) b12c8ba4: PASS: an unopenable log leaves the stderr the game was started with (exit 0, gcc and clang) ``` ### Flags and settings None. ## Testing **Tested:** Linux x86-64, game-free. The new test fails on the test-only commit 928f7444 and passes at b12c8ba4, this branch rebased onto main aa95d35f, with gcc 13.3 and with clang 23.1.5 (before, it did the same at 25a3a664 and 5a5c05c0 on main 53f96d78, and at 388c926f and 6fb80632, twice each, alternating) (`python3 -I -B tests/test_log_open_failure.py`). On `land/integration` 27553c83 (main 7ff5256e with this PR and twelve other fixes merged), the thirteen fixes' own tests and the checks their fix commits edit pass, 16 of 16; the full `python3 -I -B tests/run_all.py` ran on that branch too (see the checklist). In the game: dukenheimer ran test request T-ZH5M on a Steam Deck OLED (SteamOS 3.9.5) and reported a pass on 2026-11-15 at 03:06 UTC (https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/558#issuecomment-22268). The builds were the combined build of main 7ff5256e with https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/550 to https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/558 merged in order, this PR at its head then, 6fb80632 (b12c8ba4 after the rebases, the same change by `git patch-id`), and main 7ff5256e for the before; each in a copy of a test game folder with `logs/` unwritable, started headless over SSH. Started as a launcher starts it (stderr the journal's socket), main's messages vanished and this PR's all reached the journal; started from a terminal, both builds showed them there (that start never opens the log, so it does not reach this change). The game ran past its start in all four. The tester did not report whether a stray message landed in another file on main (only that no file held the start-up messages). The branch has been rebased since the tester's run, onto main aa95d35f, with its commits re-authored under the forge's noreply address; each commit's change is identical to before by `git patch-id`, so the commits named above are the ones tested and the result stands for this head (4ad3d404). **Not tested:** Windows in the game, where the same lines run: the whole game was built and linked for Windows with llvm-mingw (see the checklist), but not run (the fixture checks POSIX descriptors and skips on Windows). A start through Steam in Game Mode with a window (the Deck's starts were headless, with the launcher's stderr made through `systemd-cat`). The causes named in Why (a `logs/` left by a run as root, a read-only game folder): the tester made `logs/` unwritable with `chmod` instead. **Must be tested:** None ## Risks **What could break:** The log file is opened twice in a row (`fopen`, close, `freopen`); both truncate it, so nothing changes for a log that can be written. On Windows the same lines run; they are built and linked there, not yet run. **Turning it off:** revert the commit **Compatibility:** Nothing changes ## Checklist ### Everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`) of the status line's type. - [x] Why gives the reason plainly and links the issues that fit (the tracker searched). - [x] Measurements has a verdict, or one line saying why there is nothing to measure. - [x] Testing names what was tested, what wasn't, and what must still be tested, where and how. - [x] Rebased on current `main`. Onto main aa95d35f with no conflict, the commits re-authored under the forge's noreply address: each of the three commits' changes (the test, the fix, the `docs/tested/` file) is identical to before by `git patch-id`. Since 53f96d78, main changed `host/hos_main.c` in https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/729 (9e6f231d), away from this PR's lines. - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). Run on this branch at 4d8f3d76 on main 53f96d78, before this rebase (4ad3d404 now, the same patches by `git patch-id`; the Vulkan and SPIR-V headers from the CPM cache, 8 at once, on a loaded Linux x86-64 server): 531 passed, 15 failed, 48 skipped. The 15 are the ones that fail on main 53f96d78 in the same run: `test_appimage_player_build`, `test_bake_portability`, `test_cpm_discovery`, `test_crash_dialog`, `test_env_on`, `test_lift_cache`, `test_lift_cleanup_error`, `test_make_game_cache`, `test_pipeline_tour`, `test_readback_late`, `test_vk_pgo`, `test_vram_budget_row`, `test_walk_key`, `nx2/test_mesh_first_types.py` and `nx2/test_texture_init_barrier.py`. Main's run also timed out `test_fpx_sticky` (it passes alone there). After the rebase onto main aa95d35f, at 4ad3d404: 36 checks, this PR's test and those that read its changed files, with g++ 13.3 and with clang 23.1.5: 33 pass; the 3 that fail fail on main aa95d35f too (run alone there, both compilers): `tests/test_crash_dialog.py` (https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/626), `tests/test_lift_cache.py` (https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/628), `tests/test_pipeline_tour.py` (https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/627). - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### When it applies - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported. The game-free test twice each, alternating. The Steam Deck check is a behaviour check with an all-or-nothing outcome: one start of each build in each of the two ways. - [x] A file in `docs/tested/` for each new measured run (format in `docs/tested-configurations.md`). `docs/tested/2026-11-15-log-open-failure-dukenheimer-t-zh5m.md`, the tester's run; `scripts/tested_configurations.py` and `tests/test_tested_configurations.py` pass with it. - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. Linux: the game linked, in the tester's combined build (x86-64-v3, LLVM 23.1.5), and it ran. Windows: the whole game built from this branch's tip 4d8f3d76 (before this rebase; the same patches as 4ad3d404 by `git patch-id`) on Linux with `RX_TARGET=windows` and llvm-mingw 20261104 (`nx2/make_game.py --no-prepare-shaders`, tools.lock's Windows `cpm` hookshot-v2.1.0-dav1d-1.5.4, this server's CPU as the target, no wgpu-native): `longshot.exe` (431 MB) and `longshot_vk.dll` linked, not run. After the rebase onto main aa95d35f, `host/hos_main.c` at 4ad3d404 checked whole (`-fsyntax-only -Wall -Wextra` with the host's include paths) for Linux (clang 23.1.5) and Windows (llvm-mingw 20261104): the same warnings as main aa95d35f's, none new.
Assisted-by: Claude
open longshot_log.txt before reopening stderr on it, so a log that cannot be written keeps stderr
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 5s
beanstalk/approval Test farm not requested
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
6fb80632a5
Assisted-by: Claude
Author
Contributor

Filled in: the full game-free suite on the combined branch, the Windows and Linux builds, and the test request T-ZH5M. Still work in progress until a Steam Deck run is in.

Filled in: the full game-free suite on the combined branch, the Windows and Linux builds, and the test request T-ZH5M. Still work in progress until a Steam Deck run is in.
Contributor

Test request T-ZH5M: pass

Built in a combined build of main 7ff5256e with #550 to #558 merged in order, this PR at its head 6fb80632; main 7ff5256e for the
before. Setup below.

Result, with logs/ not writable (chmod a-w logs) in every start:

start build stderr where the start-up messages went log file the game
from a terminal this PR a terminal the terminal: 595 lines none ran: frames 227 -> 553 in 5 s
from a terminal main a terminal the terminal: 603 lines none ran: 185 -> 516
as a launcher starts it this PR the journal's socket the journal: all 630 lines none ran: 320 -> 646
as a launcher starts it main the journal's socket nowhere: 0 lines in the journal, and no file held them none ran: 226 -> 552
  • From a terminal, as the request asks: the start-up messages appear in the terminal with this PR, and the game runs. The first
    10 lines are below.
    • Main shows them too. The game opens longshot_log.txt only when stderr is neither a terminal nor a file (on Linux: /dev/null or
      a socket), so a start from a terminal never reaches this change.
  • With stderr on the journal, as a desktop launcher gives it: this is the case the PR changes.
    • On main the messages vanished. Nothing reached the journal, and no file in the game folder, the data folder, /tmp or /dev/shm
      held them.
    • With this PR all 630 lines reached the journal, starting with the same lines as the terminal.
  • In all four starts the game ran past its start: the frame count went up by about 330 in 5 s, in the intro.
The first 10 lines on the terminal (this PR; main's are the same apart from the source hash)
[hos host] longshot unknown (source 15ee0b2f)
[hos host] run_id: 062ee4238a34f071aed4472c8c29cd92
[hos host] control: listening on <socket> (HOS_CONTROL: this user only, no TCP; guest memory off; docs/control.md)
[hos host] mem: the guest window is a memfd's (7), shared with the GPU as dma-bufs
[hos host] oe: output resolution 1280x720
[hos host] boot: image 96 MB at 0x80000000, 9809 MB of memory for the game
[hos host] boot: 14798 MB of memory, 8422 MB of swap
[hos host] vk: reserved core mask (fallback): 0xc0
[hos MainThread] vk: the Output resolution's sizes: 6 (the screen's modes, with the common sizes of their shapes, and the game's own), and one of the player's own; shapes 16:9, 32:9
[hos MainThread] vk: device 0: AMD Custom GPU 0932 (RADV VANGOGH), Vulkan 1.4, driver 0x6801063, type 1

Setup

  • Steam Deck OLED: AMD Custom APU 0932 (RADV VANGOGH), 16 GB; Mesa 26.2.0-devel git-5c65d4fc31; SteamOS 3.9.5 (build 20261023.100),
    kernel 7.2.7-valve2.
  • Builds: main 7ff5256e, and the combined build: main + #550 to #558, merged in order, all clean. Both for x86-64-v3 with the
    release play profile and LLVM 23.1.5.
  • Each build in a copy of a test game folder, logs/ made unwritable. XDG_DATA_HOME pointed at an empty test folder.
  • Started headless (HOS_HEADLESS=1, no window), from a shell over SSH:
    • "from a terminal": in a pseudo-terminal (script);
    • "as a launcher starts it": through systemd-cat, so stderr is the journal's socket.
  • Each start was closed through the control bridge once it ran.
Test request T-ZH5M: pass Built in a combined build of main 7ff5256e with #550 to #558 merged in order, this PR at its head 6fb80632; main 7ff5256e for the before. Setup below. **Result**, with `logs/` not writable (`chmod a-w logs`) in every start: | start | build | stderr | where the start-up messages went | log file | the game | |---|---|---|---|---|---| | from a terminal | this PR | a terminal | the terminal: 595 lines | none | ran: frames 227 -> 553 in 5 s | | from a terminal | main | a terminal | the terminal: 603 lines | none | ran: 185 -> 516 | | as a launcher starts it | this PR | the journal's socket | the journal: all 630 lines | none | ran: 320 -> 646 | | as a launcher starts it | main | the journal's socket | **nowhere**: 0 lines in the journal, and no file held them | none | ran: 226 -> 552 | - **From a terminal, as the request asks:** the start-up messages appear in the terminal with this PR, and the game runs. The first 10 lines are below. - Main shows them too. The game opens `longshot_log.txt` only when stderr is neither a terminal nor a file (on Linux: /dev/null or a socket), so a start from a terminal never reaches this change. - **With stderr on the journal**, as a desktop launcher gives it: this is the case the PR changes. - On main the messages vanished. Nothing reached the journal, and no file in the game folder, the data folder, /tmp or /dev/shm held them. - With this PR all 630 lines reached the journal, starting with the same lines as the terminal. - In all four starts the game ran past its start: the frame count went up by about 330 in 5 s, in the intro. <details><summary>The first 10 lines on the terminal (this PR; main's are the same apart from the source hash)</summary> ``` [hos host] longshot unknown (source 15ee0b2f) [hos host] run_id: 062ee4238a34f071aed4472c8c29cd92 [hos host] control: listening on <socket> (HOS_CONTROL: this user only, no TCP; guest memory off; docs/control.md) [hos host] mem: the guest window is a memfd's (7), shared with the GPU as dma-bufs [hos host] oe: output resolution 1280x720 [hos host] boot: image 96 MB at 0x80000000, 9809 MB of memory for the game [hos host] boot: 14798 MB of memory, 8422 MB of swap [hos host] vk: reserved core mask (fallback): 0xc0 [hos MainThread] vk: the Output resolution's sizes: 6 (the screen's modes, with the common sizes of their shapes, and the game's own), and one of the player's own; shapes 16:9, 32:9 [hos MainThread] vk: device 0: AMD Custom GPU 0932 (RADV VANGOGH), Vulkan 1.4, driver 0x6801063, type 1 ``` </details> **Setup** - Steam Deck OLED: AMD Custom APU 0932 (RADV VANGOGH), 16 GB; Mesa 26.2.0-devel git-5c65d4fc31; SteamOS 3.9.5 (build 20261023.100), kernel 7.2.7-valve2. - **Builds:** main 7ff5256e, and the combined build: main + #550 to #558, merged in order, all clean. Both for x86-64-v3 with the release play profile and LLVM 23.1.5. - **Each build in a copy of a test game folder**, `logs/` made unwritable. XDG_DATA_HOME pointed at an empty test folder. - **Started headless** (`HOS_HEADLESS=1`, no window), from a shell over SSH: - "from a terminal": in a pseudo-terminal (`script`); - "as a launcher starts it": through `systemd-cat`, so stderr is the journal's socket. - Each start was closed through the control bridge once it ran.
zeldaootr force-pushed log-open-failure-keeps-stderr from 6fb80632a5
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 5s
beanstalk/approval Test farm not requested
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to 4d8f3d7610
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 15s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Guide screenshots / Guide images and local UI source review (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
2026-11-15 05:35:32 +00:00
Compare
Author
Contributor

Thank you, dukenheimer, for T-ZH5M on the Steam Deck (#558 (comment)), with the launcher's stderr through systemd-cat.

Rebased onto main 53f96d78 with no conflict: the test and fix commits are the same as before (git range-diff: 388c926f is now 25a3a664, 6fb80632 is now 5a5c05c0), and the run is added as docs/tested/2026-11-15-log-open-failure-dukenheimer-t-zh5m.md in a third commit, 4d8f3d76. The description now ticks the rebase and docs/tested/ boxes.

Tests run again after the rebase: tests/test_log_open_failure.py fails at 25a3a664 (descriptor 2 closed) and passes at the tip, and tests/test_udmabuf_warning.py passes at both, with gcc 13.3 and with clang 23.1.5. The full python3 -I -B tests/run_all.py on this branch: 531 passed, 15 failed, 48 skipped, the same 15 that fail on main 53f96d78 in the same run (named in the checklist). host/hos_main.c checked whole for Linux and Windows with the build's host flags, the same warnings as main's, none new. The whole game is now built and linked for Windows from the tip (RX_TARGET=windows, llvm-mingw 20261104, tools.lock's Windows cpm): longshot.exe and longshot_vk.dll linked, not run. That was the box left open, so the Windows link is no longer under Must be tested, and the description now ticks the host code box too.

Why the Deck result still holds: main changed nothing in the lines this PR changes. Since the tested base 7ff5256e, main changed host/hos_main.c in three commits, all elsewhere in the file: the crash window (a0dac82e): a check first in main that returns at once unless the game was started as the window's helper, and a watcher started after the log is opened and off headless, as the tester's starts were; HOS_HLE_STATS (1c9958c1, off unless set); and the removal of the Windows write watch and LONGSHOT_OCCLUSION_OFF (414f56e2). None of them opens the log or touches stderr before the lines this PR changes.

Thank you, dukenheimer, for T-ZH5M on the Steam Deck (https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/558#issuecomment-22268), with the launcher's stderr through `systemd-cat`. Rebased onto main 53f96d78 with no conflict: the test and fix commits are the same as before (`git range-diff`: 388c926f is now 25a3a664, 6fb80632 is now 5a5c05c0), and the run is added as `docs/tested/2026-11-15-log-open-failure-dukenheimer-t-zh5m.md` in a third commit, 4d8f3d76. The description now ticks the rebase and `docs/tested/` boxes. Tests run again after the rebase: `tests/test_log_open_failure.py` fails at 25a3a664 (descriptor 2 closed) and passes at the tip, and `tests/test_udmabuf_warning.py` passes at both, with gcc 13.3 and with clang 23.1.5. The full `python3 -I -B tests/run_all.py` on this branch: 531 passed, 15 failed, 48 skipped, the same 15 that fail on main 53f96d78 in the same run (named in the checklist). `host/hos_main.c` checked whole for Linux and Windows with the build's host flags, the same warnings as main's, none new. The whole game is now built and linked for Windows from the tip (`RX_TARGET=windows`, llvm-mingw 20261104, tools.lock's Windows `cpm`): `longshot.exe` and `longshot_vk.dll` linked, not run. That was the box left open, so the Windows link is no longer under Must be tested, and the description now ticks the host code box too. Why the Deck result still holds: main changed nothing in the lines this PR changes. Since the tested base 7ff5256e, main changed `host/hos_main.c` in three commits, all elsewhere in the file: the crash window (a0dac82e): a check first in `main` that returns at once unless the game was started as the window's helper, and a watcher started after the log is opened and off headless, as the tester's starts were; `HOS_HLE_STATS` (1c9958c1, off unless set); and the removal of the Windows write watch and `LONGSHOT_OCCLUSION_OFF` (414f56e2). None of them opens the log or touches stderr before the lines this PR changes.
zeldaootr changed title from WIP: fix: a log file that cannot be opened no longer closes stderr to fix: a log file that cannot be opened no longer closes stderr 2026-11-15 05:35:34 +00:00
zeldaootr force-pushed log-open-failure-keeps-stderr from 4d8f3d7610
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 15s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Guide screenshots / Guide images and local UI source review (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
to 4ad3d404ed
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Guide screenshots / Guide images and local UI source review (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 4s
2026-11-15 17:12:06 +00:00
Compare
Author
Contributor

Rebased onto main aa95d35f (head 4ad3d404); the commits are re-authored with the forge's noreply address: no code change, the same patches by git patch-id.
Tests re-run on the new base, gcc 13.3 and clang 23.1.5: test_log_open_failure fails at the test-only commit 928f7444 (stderr closed) and passes at b12c8ba4; of the 36 checks that are this PR's or read its files, 33 pass and the 3 that fail fail on main too. The Deck run (T-ZH5M) stands; the description names the new commits.

Rebased onto `main` aa95d35f (head 4ad3d404); the commits are re-authored with the forge's noreply address: no code change, the same patches by `git patch-id`. Tests re-run on the new base, gcc 13.3 and clang 23.1.5: `test_log_open_failure` fails at the test-only commit 928f7444 (stderr closed) and passes at b12c8ba4; of the 36 checks that are this PR's or read its files, 33 pass and the 3 that fail fail on main too. The Deck run (T-ZH5M) stands; the description names the new commits.
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Guide screenshots / Guide images and local UI source review (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 4s
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
Some workflows are waiting to be reviewed.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'log-open-failure-keeps-stderr:zeldaootr-log-open-failure-keeps-stderr'
git switch 'zeldaootr-log-open-failure-keeps-stderr'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!558
No description provided.