fix: a log file that cannot be opened no longer closes stderr #558
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!558
Loading…
Reference in a new issue
No description provided.
Delete branch "zeldaootr/Longshot:log-open-failure-keeps-stderr"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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(alogs/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 LinuxSettings 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 openlongshot_log.txtSteps: start the game through
systemd-cat, so stderr is the journal's socket, and read the journalExpected: 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.
game_main()inhost/hos_main.copens the log withfopenfirst and only then reopens stderr on it withfreopen.freopencloses stderr when its open fails, which freed descriptor 2 for the next file the game opened (the guest memory's memfd).tests/test_log_open_failure.pycuts that code out ofhos_main.c, makes the log's path unopenable (a file namedlogswhere the folder would be) and checks that descriptor 2 is still open; a second run checks the log is opened inlogs/.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_HOMEan empty test folder; started headless (HOS_HEADLESS=1) from a shell over SSH, closed through the control bridge once it ranMethod: 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 throughsystemd-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.pyat928f7444and atb12c8ba4(rebased onto mainaa95d35f), with gcc 13.3 and with clang 23.1.5; before that, at25a3a664and5a5c05c0on main53f96d78, and before the first rebase at388c926fand6fb80632, twice each, alternatingHow to reproduce: build main
7ff5256eand the combined build (main7ff5256ewith #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 commit7ff5256e6fb80632(in the combined build)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)
aa95d35f(with the test,928f7444)b12c8ba4Flags and settings
None.
Testing
Tested: Linux x86-64, game-free. The new test fails on the test-only commit
928f7444and passes atb12c8ba4, this branch rebased onto mainaa95d35f, with gcc 13.3 and with clang 23.1.5 (before, it did the same at25a3a664and5a5c05c0on main53f96d78, and at388c926fand6fb80632, twice each, alternating) (python3 -I -B tests/test_log_open_failure.py). Onland/integration27553c83 (main7ff5256ewith this PR and twelve other fixes merged), the thirteen fixes' own tests and the checks their fix commits edit pass, 16 of 16; the fullpython3 -I -B tests/run_all.pyran 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
7ff5256ewith #550 to #558 merged in order, this PR at its head then,6fb80632(b12c8ba4after the rebases, the same change bygit patch-id), and main7ff5256efor the before; each in a copy of a test game folder withlogs/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 bygit 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 (alogs/left by a run as root, a read-only game folder): the tester madelogs/unwritable withchmodinstead.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
type(scope): description) of the status line's type.main. Onto mainaa95d35fwith no conflict, the commits re-authored under the forge's noreply address: each of the three commits' changes (the test, the fix, thedocs/tested/file) is identical to before bygit patch-id. Since53f96d78, main changedhost/hos_main.cin #729 (9e6f231d), away from this PR's lines.python -I -B tests/run_all.pypasses (any failure that also fails onmainis named). Run on this branch at4d8f3d76on main53f96d78, before this rebase (4ad3d404now, the same patches bygit 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 main53f96d78in 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.pyandnx2/test_texture_init_barrier.py. Main's run also timed outtest_fpx_sticky(it passes alone there). After the rebase onto mainaa95d35f, at4ad3d404: 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 mainaa95d35ftoo (run alone there, both compilers):tests/test_crash_dialog.py(#626),tests/test_lift_cache.py(#628),tests/test_pipeline_tour.py(#627).third_party/untouched (a new folder with aPROVENANCE.mdinstead).When it applies
Assisted-by:/Co-Authored-By:trailers.docs/tested/for each new measured run (format indocs/tested-configurations.md).docs/tested/2026-11-15-log-open-failure-dukenheimer-t-zh5m.md, the tester's run;scripts/tested_configurations.pyandtests/test_tested_configurations.pypass with it.mainand passing here.tests/that reads changed source updated in this PR, not weakened.4d8f3d76(before this rebase; the same patches as4ad3d404bygit patch-id) on Linux withRX_TARGET=windowsand llvm-mingw 20261104 (nx2/make_game.py --no-prepare-shaders, tools.lock's Windowscpmhookshot-v2.1.0-dav1d-1.5.4, this server's CPU as the target, no wgpu-native):longshot.exe(431 MB) andlongshot_vk.dlllinked, not run. After the rebase onto mainaa95d35f,host/hos_main.cat4ad3d404checked whole (-fsyntax-only -Wall -Wextrawith 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.longshot_log.txtcloses stderr and frees descriptor 2 388c926ff3longshot_log.txtbefore reopening stderr on it, so a log that cannot be written keeps stderrFilled 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.
Test request T-ZH5M: pass
Built in a combined build of main
7ff5256ewith #550 to #558 merged in order, this PR at its head6fb80632; main7ff5256efor thebefore. Setup below.
Result, with
logs/not writable (chmod a-w logs) in every start:10 lines are below.
longshot_log.txtonly when stderr is neither a terminal nor a file (on Linux: /dev/null ora socket), so a start from a terminal never reaches this change.
held them.
The first 10 lines on the terminal (this PR; main's are the same apart from the source hash)
Setup
kernel 7.2.7-valve2.
7ff5256e, and the combined build: main + #550 to #558, merged in order, all clean. Both for x86-64-v3 with therelease play profile and LLVM 23.1.5.
logs/made unwritable. XDG_DATA_HOME pointed at an empty test folder.HOS_HEADLESS=1, no window), from a shell over SSH:script);systemd-cat, so stderr is the journal's socket.6fb80632a54d8f3d7610Thank you, dukenheimer, for T-ZH5M on the Steam Deck (#558 (comment)), with the launcher's stderr through
systemd-cat.Rebased onto main
53f96d78with no conflict: the test and fix commits are the same as before (git range-diff:388c926fis now25a3a664,6fb80632is now5a5c05c0), and the run is added asdocs/tested/2026-11-15-log-open-failure-dukenheimer-t-zh5m.mdin a third commit,4d8f3d76. The description now ticks the rebase anddocs/tested/boxes.Tests run again after the rebase:
tests/test_log_open_failure.pyfails at25a3a664(descriptor 2 closed) and passes at the tip, andtests/test_udmabuf_warning.pypasses at both, with gcc 13.3 and with clang 23.1.5. The fullpython3 -I -B tests/run_all.pyon this branch: 531 passed, 15 failed, 48 skipped, the same 15 that fail on main53f96d78in the same run (named in the checklist).host/hos_main.cchecked 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 Windowscpm):longshot.exeandlongshot_vk.dlllinked, 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 changedhost/hos_main.cin three commits, all elsewhere in the file: the crash window (a0dac82e): a check first inmainthat 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 andLONGSHOT_OCCLUSION_OFF(414f56e2). None of them opens the log or touches stderr before the lines this PR changes.WIP: fix: a log file that cannot be opened no longer closes stderrto fix: a log file that cannot be opened no longer closes stderr4d8f3d76104ad3d404edRebased onto
mainaa95d35f(head4ad3d404); the commits are re-authored with the forge's noreply address: no code change, the same patches bygit patch-id.Tests re-run on the new base, gcc 13.3 and clang 23.1.5:
test_log_open_failurefails at the test-only commit928f7444(stderr closed) and passes atb12c8ba4; 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.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.