Skip to content

fix(linux): run an "Open application" target instead of xdg-opening it - #839

Open
4ni1ak wants to merge 3 commits into
AprilNEA:masterfrom
4ni1ak:fix/linux-open-application
Open

fix(linux): run an "Open application" target instead of xdg-opening it#839
4ni1ak wants to merge 3 commits into
AprilNEA:masterfrom
4ni1ak:fix/linux-open-application

Conversation

@4ni1ak

@4ni1ak 4ni1ak commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

An Actions Ring slot configured with an application did nothing on Linux.

Split out of #802 at your request.

Fixes #775

Changes

Action::OpenApplication handed its target straight to opener::open, which on
Linux is xdg-open — and xdg-open opens a file. Given /usr/bin/nautilus it
looks for a handler claiming application/x-executable, finds none, and does
nothing, so the slot looked dead.

On Linux the target is now classified first:

  • an executable file path, or a bare command name resolved on PATH, is spawned
    as a program;
  • URLs, folders, documents and .desktop entries stay with the opener — a
    desktop entry especially, since xdg-open reads its Exec= and honours
    Terminal= and StartupNotify=, none of which spawning the file would.

macOS and Windows keep going through the opener, where open and
ShellExecute already run .app bundles and .exes.

The child is waited on from a detached thread, so a long-lived GUI app is not
left as a zombie for the agent's lifetime. The decision table takes its two
filesystem probes as parameters, so it is unit-tested with fakes rather than
against the host.

Testing

Linux, x86_64, Rust 1.98.0:

cargo test -p openlogi-inject
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings           # RUSTFLAGS=-D warnings
cargo clippy --target x86_64-pc-windows-gnu -p openlogi-inject \
  --all-targets -- -D warnings                                  # cfg cross-check
cargo test --workspace

All green. New tests:
executables_are_run_and_everything_else_goes_to_the_opener,
an_executable_desktop_entry_still_goes_to_the_opener, and
the_real_probes_resolve_a_shell_on_path, which pins the two real filesystem
probes against sh on PATH rather than only the fakes.

Not run on this host: tests (macos), cargo-deny, macOS clippy — no macOS
SDK here. The Windows cross-lint covers the non-Linux side of the one
#[cfg(target_os = "linux")] statement added to inject.rs.

Not runtime-tested. To verify: configure /usr/bin/nautilus and bare
nautilus in an Actions Ring slot — both should launch the file manager — and
confirm a folder path and an https:// URL still open as before.

@greptile-apps

greptile-apps Bot commented Aug 23, 2026

Copy link
Copy Markdown

Greptile Summary

The PR changes Linux application-target handling so executable paths and commands found on PATH are spawned directly, while URLs, documents, directories, and desktop entries continue through the desktop opener.

  • Adds Linux-specific target classification and PATH resolution.
  • Spawns programs synchronously, then waits for successful children on a detached thread.
  • Adds tests for executable classification, desktop entries, PATH lookup, and spawn failures.

Confidence Score: 4/5

The PR is not yet safe to merge because non-permission spawn failures still repeat the failed executable launch through the desktop opener.

A target that passes the execute-bit check but fails to spawn for reasons other than permission denial is still passed unchanged to the opener, which does not run executable files and therefore leaves the Actions Ring slot inert.

Files Needing Attention: crates/openlogi-inject/src/inject/linux.rs, crates/openlogi-inject/src/inject.rs

Important Files Changed

Filename Overview
crates/openlogi-inject/src/inject.rs Routes Linux OpenApplication targets through launch_program before retaining the existing desktop-opener fallback.
crates/openlogi-inject/src/inject/linux.rs Adds executable classification and direct spawning, but non-permission spawn failures still send the unchanged executable to the opener, leaving the previously reported fallback defect outstanding.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[OpenApplication target] --> B{Linux target classification}
    B -->|URL, document, directory, desktop entry| C[Desktop opener]
    B -->|Executable path or PATH command| D[Command spawn]
    D -->|Success| E[Detached child wait]
    D -->|Permission denied| F[Log and report handled]
    D -->|Other spawn error| C
Loading

Reviews (5): Last reviewed commit: "fix(linux): don't hand an unexecutable p..." | Re-trigger Greptile

Comment thread crates/openlogi-inject/src/inject/linux.rs
@4ni1ak
4ni1ak force-pushed the fix/linux-open-application branch from 70a1fd7 to 19a9ab7 Compare August 24, 2026 19:23
Comment thread crates/openlogi-inject/src/inject/linux.rs
@davidbudnick davidbudnick added type: bug Something is broken or behaves incorrectly platform: linux Linux-specific issue labels Aug 25, 2026
4ni1ak added 3 commits August 25, 2026 18:13
`Action::OpenApplication` handed its target straight to `opener::open`,
which on Linux is `xdg-open` — and xdg-open *opens* a file. Given
`/usr/bin/nautilus` it looks for a handler claiming
`application/x-executable`, finds none, and does nothing at all, so the
Actions Ring slot appeared dead.

On Linux, classify the target first: an executable file path or a bare
command name resolved on `PATH` is spawned as a program; URLs, folders,
documents, and `.desktop` entries stay with the opener — a desktop entry
especially, since xdg-open reads its `Exec=` and honours `Terminal=` and
`StartupNotify=`, none of which spawning the file would. macOS and Windows
keep going through the opener, where `open`/`ShellExecute` already run
`.app` bundles and `.exe`s.

The child is waited on from a detached thread so a long-lived app does not
sit as a zombie for the agent's lifetime. The decision table takes its two
filesystem probes as parameters, so it is tested with fakes; one further
test pins the real probes against `sh` on `PATH`.

Fixes AprilNEA#775
`is_executable` is a mode check, so a regular file carrying an execute bit
for another user classified as a program. The spawn then failed inside the
detached thread, after `launch_program` had already reported the target
handled, so the opener fallback was skipped and activating the slot did
nothing at all.

Run the spawn on the calling thread and let its result decide the return
value; only the wait stays detached, which is all it was ever needed for.
`Command::spawn` reports a refused `exec` rather than succeeding and failing
later, so a target this user cannot run — or one that is not a loadable
binary, or vanished between the check and the call — now falls through to
the desktop opener like any other file.

The mode check stays as the classification heuristic and says so: the exec
is the authority, and this is the layer that defers to it.

Found by the Greptile review on this PR.
The previous fallback treated every refused spawn the same and passed the
target to `xdg-open`. That helps when the mode bit was misleading — a data
file with the bit set, a script with no interpreter — but not when the exec
was refused for permissions: the opener cannot run it either, and its best
case is a binary opened in a text editor.

Split on the error. `PermissionDenied` is reported handled, with a warning
saying the target is not executable by this user; everything else still
falls through, because those really are the opener's business.

Found by the Greptile review on this PR.
@4ni1ak
4ni1ak force-pushed the fix/linux-open-application branch from 1d69177 to 290242b Compare August 25, 2026 15:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform: linux Linux-specific issue type: bug Something is broken or behaves incorrectly

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: The action configured via "Open application" in the Actions Ring does not work (e.g., /usr/bin/nautilus, nautilus)

2 participants