Repository navigation
fix(unix): restore launcher fallback and support Plasma 6 - #135
Conversation
<!-- agent --> When `xdg-open` starts but exits unsuccessfully, `that()` and the CLI previously stopped before trying another installed opener. On Plasma 6, the fallback list also omitted the versioned KDE launchers. ### Launcher behavior - Retry reported failures from UNIX desktop launchers and retain the first unsuccessful exit when later candidates are missing. - Add `kde-open6` and `kde-open5` before the existing `kde-open` fallback. - Keep launcher ordering, argument boundaries, and exit-status policy together so compatibility decisions are explicit. - Preserve Windows' stop-on-failure policy and the existing protections for option-shaped and non-UTF-8 paths. ### Bounded detached checks Add `that_detached_with_timeout(path, timeout)` to opt into checking the actual launcher status for a configurable interval per candidate. Retry confirmed UNIX launcher failures; consider successful exits or launchers still running at the deadline handed off. A zero timeout polls once. On UNIX, use `setsid()` while retaining the launcher as a child. Reserve a background waiter before spawning and transfer ownership after a timeout so the launcher can outlive its caller and is eventually reaped. Stop fallback if observing an already-started launcher fails, avoiding a second launch based only on a wait error. Existing detached calls retain their timing behavior. The new API uses command-based launchers on Windows, including when `shellexecute-on-windows` is enabled. ### Historical constraints `55dc10d` relaxed exit checks for argumentless availability probes, where usage errors are expected. It did not establish that failed real opening operations should stop fallback. `245c95e` introduced that behavior; the earlier implementation retried unsuccessful operations. Keep stderr redirected to null: `30a144a` and `0bdc6d6` document previous pipe deadlocks and hangs. Do not interpret the successful double-fork intermediate child's exit as launcher success (`a78c0ca`, `43e601d`). Document these constraints and upstream launcher behavior in `docs/launchers.md`. Exclude `kioclient exec`, which enables execution beyond `kde-open`'s behavior, and arbitrary file-manager or AppImage environment fallbacks that could override configured handlers. ### Validation and limits - Default and all-feature Linux tests pass, covering fallback, error selection, Plasma 6 reachability, timeouts, child reaping, survival after caller exit, large stderr output, argument protection, and CLI behavior. - Formatting, `git diff --check`, and all-target, all-feature Linux clippy pass. Windows and macOS compile checks and clippy also pass. - Actual desktop and AppImage behavior still needs testing. A handler that opens a target before reporting failure can cause a duplicate open when retried. Failures after the timeout cannot trigger fallback. Refs: #134 Assisted-by: GPT 6.0 Astra Co-authored-by: GPT 6.0 Astra <codex@openai.com>
12db1c3 to
279c1b2
Compare
|
Will merge once @Alfagun74 could give it a shot. |
There was a problem hiding this comment.
🟡 Changes recommended
Timeout polling can retry failures after the deadline, and the reaping assertion checks the wrong thread.
2 open issues
What changed in this PR
Restores UNIX launcher fallback and adds Plasma 6 support, addressing #134 while preserving Windows’ exit-status policy.
Changes:
- Adds versioned KDE launchers and retries unsuccessful UNIX launches.
- Introduces bounded detached status checks with background child reaping.
- Adds fallback, timeout, argument-safety, and CLI regression tests.
| File | Description |
|---|---|
| tests/unix_launchers.rs | Adds launcher integration tests. |
| src/unix.rs | Centralizes launcher policies and adds KDE candidates. |
| src/main.rs | Retries failed UNIX launchers in the CLI. |
| src/lib.rs | Adds shared fallback handling and the detached timeout API. |
Review effort: Balanced
|
Tested
Integration tests, unit tests, doc tests and The remaining gap: Both Copilot findings look right. If the deadline is only checked while the child is still running, a launcher that fails just after the timeout can trigger a second launch and a duplicate open. And the reaping test has to read AppImage: keeping Skipping I'll test the GameVault build on a real Plasma 6 machine and report back. |
|
Thanks for testing. Can you provide a patch for this? I can't easily test it. |
`that_detached()` returned as soon as a launcher spawned, so a launcher that failed right after starting was reported as success and no fallback ran. Observe each launcher for 250 ms by delegating to `that_detached_with_timeout()`. A launcher that exits unsuccessfully within that window falls through to the next candidate; one still running is treated as handed off. The macOS and `shellexecute-on-windows` paths are unchanged. Extend the fixture tests to call `that_detached()` directly. Refs: #134
|
Opened #136 against
|
553d3b9 to
817de63
Compare
<!-- agent --> - The deadline is checked only after `try_wait()` reports a running child. With a 50 ms timeout, if the polling thread resumes at 60 ms after the launcher failed at 55 ms, this returns `Exited` and triggers fallback. That contradicts the documented policy that failures after the timeout cannot trigger fallback and can cause a duplicate open. Check the deadline before subsequent polls, preserving the initial poll for a zero timeout. - Linux tracks children per thread. `std::process::id()` selects the main thread, but the test harness runs `launcher_fixture` on a worker thread that spawns the launcher. This assertion can therefore pass even if that launcher remains an unreaped zombie. Read `/proc/thread-self/children` to check the spawning thread instead. Assisted-by: GPT 6.1 Sol Co-authored-by: GPT 6.1 Sol <codex@openai.com>
817de63 to
c88759b
Compare

Tasks
When
xdg-openstarts but exits unsuccessfully,that()and the CLIpreviously stopped before trying another installed opener. On Plasma 6,
the fallback list also omitted the versioned KDE launchers.
Launcher behavior
unsuccessful exit when later candidates are missing.
kde-open6andkde-open5before the existingkde-openfallback.together so compatibility decisions are explicit.
for option-shaped and non-UTF-8 paths.
Bounded detached checks
Add
that_detached_with_timeout(path, timeout)to opt into checking theactual launcher status for a configurable interval per candidate. Retry
confirmed UNIX launcher failures; consider successful exits or launchers
still running at the deadline handed off. A zero timeout polls once.
On UNIX, use
setsid()while retaining the launcher as a child. Reserve abackground waiter before spawning and transfer ownership after a timeout
so the launcher can outlive its caller and is eventually reaped. Stop
fallback if observing an already-started launcher fails, avoiding a second
launch based only on a wait error. Existing detached calls retain their
timing behavior. The new API uses command-based launchers on Windows,
including when
shellexecute-on-windowsis enabled.Historical constraints
55dc10drelaxed exit checks for argumentless availability probes, whereusage errors are expected. It did not establish that failed real opening
operations should stop fallback.
245c95eintroduced that behavior;the earlier implementation retried unsuccessful operations.
Keep stderr redirected to null:
30a144aand0bdc6d6document previouspipe deadlocks and hangs. Do not interpret the successful double-fork
intermediate child's exit as launcher success (
a78c0ca,43e601d).Document these constraints and upstream launcher behavior in
docs/launchers.md. Excludekioclient exec, which enables executionbeyond
kde-open's behavior, and arbitrary file-manager or AppImageenvironment fallbacks that could override configured handlers.
Validation and limits
selection, Plasma 6 reachability, timeouts, child reaping, survival after
caller exit, large stderr output, argument protection, and CLI behavior.
git diff --check, and all-target, all-feature Linux clippypass. Windows and macOS compile checks and clippy also pass.
that opens a target before reporting failure can cause a duplicate open
when retried. Failures after the timeout cannot trigger fallback.
Refs: #134