mirror of
https://github.com/outbackdingo/hyprcosmic.git
synced 2026-08-25 07:10:09 +00:00
Running a second cosmic-session on the development machine turns out to be
genuinely dangerous, and twice it logged the developer out mid-session and
destroyed open work. Two distinct causes, both encoded here as guards:
- Name-based process selection cannot distinguish the fork, the system
install, or a stand-in binary; they all answer to `cosmic-session`. The
second logout came from `pgrep -x cosmic-session | head -1` inside the
test written to demonstrate that name matching is unsafe, because `head
-1` favours the oldest match, which is always the live desktop. The
harness therefore never selects a process by name: it spawns under
setsid and signals `-$PGID`, with the group ID taken from `$!`.
- A nested cosmic-session takes the well-known D-Bus name
com.system76.CosmicSession away from the running session on a shared bus
("Connection `:1.3` lost name ..." in the journal), destabilising the
outer desktop before anything is killed. The harness always runs under
dbus-run-session. Nesting cosmic-comp alone does not need this.
It also refuses to start without WAYLAND_DISPLAY, since the winit backend
would otherwise fall back to DRM and seize the real display, and it reaps
IPC socket directories whose owning PID is gone.
Verified: shellcheck-clean syntax; the no-WAYLAND_DISPLAY guard fires; an
audit confirms every `kill` targets the script's own process group and no
code path matches a process by name. NOT verified: the harness has never
been run against the real binaries. Session-level runtime testing is now
deferred to a VM or to logging into hyprcosmic.desktop directly, rather than
nesting inside the developer's live desktop.
cosmic-session joins cosmic-comp in .gitignore; both are forks that become
submodules under the topology in the design spec.
103 lines
4.1 KiB
Bash
Executable File
103 lines
4.1 KiB
Bash
Executable File
#!/usr/bin/bash
|
|
#
|
|
# Run the forked cosmic-session nested inside the live desktop, safely.
|
|
#
|
|
# WHY THIS SCRIPT EXISTS
|
|
# ----------------------
|
|
# On 2026-08-10 an ad-hoc version of this test logged the developer out of their
|
|
# own desktop. Two independent mistakes did it, and both are easy to repeat by
|
|
# hand, so the test lives in a script instead:
|
|
#
|
|
# 1. `pkill -x cosmic-session` matched the *real* session leader. The fork and
|
|
# the system COSMIC ship binaries with the same name, so no name-based
|
|
# match can distinguish them. This script therefore never uses pkill or
|
|
# pgrep; it kills the process group it created, by ID.
|
|
#
|
|
# 2. A nested cosmic-session on the shared session bus takes the well-known
|
|
# D-Bus name `com.system76.CosmicSession` away from the running session
|
|
# (journal: "Connection `:1.3` lost name `com.system76.CosmicSession`").
|
|
# That destabilises the outer desktop before anything is even killed. This
|
|
# script always runs under `dbus-run-session`, so the nested session gets a
|
|
# private bus and cannot touch the real one's names.
|
|
#
|
|
# Nesting cosmic-comp alone is safe and does not need any of this; the hazard is
|
|
# specific to running a second cosmic-session.
|
|
#
|
|
# Usage: tools/nested-session.sh [seconds] [-- extra env assignments]
|
|
# e.g. tools/nested-session.sh 12 -- HYPRCOSMIC_PROFILE=hyprcosmic
|
|
|
|
set -uo pipefail
|
|
|
|
REPO="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
SESSION_BIN="$REPO/cosmic-session/target/debug/cosmic-session"
|
|
COMP_BIN="$REPO/cosmic-comp/target/debug/cosmic-comp"
|
|
|
|
DURATION="${1:-12}"
|
|
shift || true
|
|
[[ "${1:-}" == "--" ]] && shift
|
|
|
|
die() { printf 'nested-session: %s\n' "$*" >&2; exit 1; }
|
|
|
|
# Refuse to run outside a Wayland session. Without a host compositor the winit
|
|
# backend would fall back to DRM and try to take over the real display.
|
|
[[ -n "${WAYLAND_DISPLAY:-}" ]] || die "no WAYLAND_DISPLAY; refusing to run (would grab the DRM device)"
|
|
[[ -x "$SESSION_BIN" ]] || die "not built: $SESSION_BIN"
|
|
[[ -x "$COMP_BIN" ]] || die "not built: $COMP_BIN"
|
|
command -v dbus-run-session >/dev/null || die "dbus-run-session is required for bus isolation"
|
|
|
|
# Record the live session's leader purely so the exit check can prove we did not
|
|
# disturb it. Asked of logind rather than matched by process name: a name-based
|
|
# lookup is what destroyed the developer's session twice, once in the very test
|
|
# written to prove name matching was unsafe. There is no `ps -C cosmic-...`
|
|
# anywhere in this file, deliberately.
|
|
OUTER_LEADER="$(loginctl show-session "${XDG_SESSION_ID:-}" -p Leader --value 2>/dev/null)"
|
|
|
|
LOG="$(mktemp -t nested-session.XXXXXX.log)"
|
|
echo "nested-session: logging to $LOG"
|
|
echo "nested-session: live session leader=$OUTER_LEADER (must survive)"
|
|
|
|
# setsid puts the whole tree in a fresh process group whose ID equals the child
|
|
# PID, so one negative kill reaps the session, the compositor and every
|
|
# component it spawned -- with no pattern matching anywhere.
|
|
setsid env \
|
|
COSMIC_BACKEND=winit \
|
|
RUST_LOG="${RUST_LOG:-info}" \
|
|
"$@" \
|
|
dbus-run-session -- "$SESSION_BIN" "$COMP_BIN" >"$LOG" 2>&1 &
|
|
PGID=$!
|
|
|
|
cleanup() {
|
|
# Negative PID = process group. Never a name.
|
|
kill -TERM -"$PGID" 2>/dev/null
|
|
for _ in $(seq 20); do
|
|
kill -0 -"$PGID" 2>/dev/null || break
|
|
sleep 0.25
|
|
done
|
|
kill -KILL -"$PGID" 2>/dev/null
|
|
|
|
# An abruptly-killed compositor leaves its IPC directory behind, so drop any
|
|
# whose owning PID is gone. Matching is on the PID embedded in the name.
|
|
for dir in "${XDG_RUNTIME_DIR:?}"/hypr/cosmic_*; do
|
|
[[ -d "$dir" ]] || continue
|
|
pid="${dir##*/cosmic_}"; pid="${pid%%_*}"
|
|
kill -0 "$pid" 2>/dev/null || rm -rf "$dir"
|
|
done
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
sleep "$DURATION"
|
|
cleanup
|
|
trap - EXIT INT TERM
|
|
|
|
# The whole point: confirm the developer still has a desktop.
|
|
status=0
|
|
if [[ -n "$OUTER_LEADER" ]] && ! kill -0 "$OUTER_LEADER" 2>/dev/null; then
|
|
echo "nested-session: FAIL - live session leader $OUTER_LEADER died during the test" >&2
|
|
status=1
|
|
else
|
|
echo "nested-session: live session survived"
|
|
fi
|
|
|
|
echo "--- log: $LOG ---"
|
|
exit $status
|