Repository navigation
Mast v0.7.6: Stop actually stops Sail commands - #55
Merged
Merged
Conversation
On Plasma under Wayland the titlebar's minimise, maximise and close did nothing — no hover, no click — and the bar stood taller than every other window's. Neither was GTK's doing: tao 0.35 replaced the window's titlebar on Wayland with its own GTK header bar, wrapped in an event box set above its children, so every pointer event landed on the box and none reached the buttons. Dragging still worked only because the window handles that itself. And being a header bar, it asked for client-side decorations, so KWin never drew its own. tao 0.36 (tauri-apps/tao#1218) dropped that header bar and leaves the choice to GTK, which asks for server-side decorations through KDE's protocol wherever the compositor offers it. Tauri 2.12 carries tao 0.37, so this moves Tauri to 2.12.3 and @tauri-apps/api with it — the release build refuses mismatched Rust and JS minors. Run against this Plasma session with WAYLAND_DEBUG, 0.7.4 sends request_mode(1), client-side; this build sends request_mode(2) and KWin answers mode(2), drawing a Breeze titlebar over a window whose geometry is now the content alone. Desktops that tile still lose the titlebar via linux_env::wants_no_titlebar, whose comment claimed GTK3 had no server-side decoration support on Wayland at all — it does, and the comment now says who draws the bar where.
CI's stable toolchain moved to Rust 1.99, whose new double_must_use lint fires on the #[must_use] async-trait 0.1.91 puts on every generated method — six errors at RuntimeAdapter's attribute, none of them in code written here, and enough to stop clippy before it reached any crate that depends on mast-docker. async-trait 0.1.92 exists to resolve exactly that lint (dtolnay/async-trait#303). With it, clippy 1.99 runs clean across the whole workspace with -D warnings.
Stopping a project command that runs through Sail — `sail artisan dev`, `sail npm run dev` — reported success and stopped nothing. The stop script finds the command's processes by the MAST_COMMAND_RUN marker in /proc/<pid>/environ, and it ran as root. Reading another user's environ takes CAP_SYS_PTRACE, which Docker's default capability set leaves out, so as root every one of the command's sail-owned processes was unreadable, the marker was never found, and the script exited 0. Since nothing was flagged as left behind, the next Start, Restart or auto-start launched a second copy beside the first. On a real project that left two `artisan dev` stacks in one container — two Horizon masters, two queue listeners, two schedulers running every task twice — and the second stack's Vite, failing on the port the first still held, deleted public/hot on its way out, so pages fell back to an old build while the command showed as running. The stop now runs as the user the command was started as, which can read and signal all of its own processes. The existing test ran the script as the processes' owner and so could never see this; a new Docker-gated test runs the real stop argv against an Alpine container with the command owned by uid 1000. Against the root stop it fails with the command still running; with this change it stops the command and leaves another run's process alone.
Stopping as the command's own user still missed the processes that matter most. Sail's PHP binary carries a file capability (cap_net_bind_service, so `artisan serve` can bind port 80), and running a binary with file capabilities makes the process non-dumpable: its /proc/<pid>/environ passes to root, readable by no one without CAP_SYS_PTRACE — not even `sail`. So the environment marker only ever found the sh and node processes around a command. For `sail artisan dev`, stopping those would have orphaned Horizon, the scheduler, the queue listener, Reverb and Pail; for a command that is PHP from the start, such as `sail artisan horizon`, it found nothing at all. Each run now starts through a small sh wrapper that records its PID and start time in /tmp/mast-runs/<run> inside the container, then execs the command, so the PID is the command's own. The stop takes the recorded process (if that PID still has the recorded start time, so a reused number is never signalled), anything carrying the run's marker, and every descendant of either, read from /proc/<pid>/stat, which anyone can read. A completed stop removes the record. The Docker test now starts its runs through their real argv, wrapper included, with a setgid copy of busybox standing in for Sail's PHP — a non-dumpable process whose environment its own user cannot read — and a child under it. Matching by environment alone, it fails with both still running; with the record and the tree it stops them and leaves another run alone. Dry-run in a real Sail container against a leftover `artisan dev` stack, it finds the 22 marked processes and the 7 PHP processes beneath them that the marker cannot see, and nothing else.
A record now reads `pid start name`, so one left behind by an earlier Mast session — a run this process never started and has no operation for — can still say which command it was, not just which PID. The name is reduced to a single field (anything outside [A-Za-z0-9._-] becomes `_`), and the stop reads only the first two fields, so anything added after them later cannot shift what it compares.
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on #54. Merge that first; this diff then narrows to its own commits.
What
sail artisan devandsail npm run dev. There were two causes:MAST_COMMAND_RUNmarker in/proc/<pid>/environ. Reading another user'senvirontakesCAP_SYS_PTRACE, which Docker's default capability set leaves out, so root could read none ofsail's processes.sailcan't read its PHP processes. Sail's PHP binary carriescap_net_bind_servicesoartisan servecan bind port 80. Running a binary with file capabilities makes the process non-dumpable, which gives itsenvironto root, and nobody withoutCAP_SYS_PTRACEcan read it. So the marker never reached Horizon, the scheduler, the queue, Reverb or Pail. For a command that is PHP from the start, likesail artisan horizon, it found nothing at all.artisan devstacks in one container, both started by one Mast process):queue:listen, and twoschedule:work, so every scheduled task ran twice.public/hot. Pages fell back to an old build while the command still showed as running.shwrapper. It recordspid start namein/tmp/mast-runs/<run>inside the container, thenexecs the command, so the recorded PID is the command's own. The name means a record left by an earlier Mast session can still say which command it was./proc/<pid>/stat, which anyone can read.Verified
stop_reaches_command_processes_no_one_can_read. Both runs start through their real argv, wrapper included. A setgid copy of busybox stands in for Sail's PHP: a non-dumpable process whoseenvironits own user can't read, with a child under it.artisan devstack. It finds the 22 marked processes and the 7 PHP processes beneath them that the marker can't see (Horizon master, supervisor and worker, Reverb, scheduler,queue:listen, Pail). It finds nothing from the current run and nothing from Sail's ownartisan serve.failed_sail_cleanup_must_succeed_before_a_later_command_can_run: its fakesailrecognised the cleanup call by-u root. It now recognises it as the one exec without a marker.cargo test -p mast-enginepasses, Docker tests included.cargo clippy -p mast-engine --all-targets -D warningsis clean on Rust 1.96 and 1.99.Not in this PR