mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 19:07:17 +01:00
vmbox: vm lightdm and vm shim — the host's login topology and fake hardware
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
The golden image logs in with agetty+startx and has no RGB controllers, no NVIDIA card and no sound card, so the night-lockdown scripts — which mask/stop lightdm.service and getty@.service and shell out to openrgb, nvidia-smi and amixer — had nothing real to act on in a sandbox and were being "verified" on the host instead. - `vm lightdm <name>`: installs lightdm + lightdm-gtk-greeter into the live overlay (pacman -Syu, not -S: the overlay's package database is the image's and mirrors have rotated those versions out), configures autologin -> i3 exactly like the host, turns tty1 back into a plain getty, reboots the guest and proves lightdm.service is active with an Xorg it owns. Never rebuilds the base: `vm build --force` re-seals a new checksum while every existing sandbox still backs onto the old file's name, which is silent qcow2 corruption. - `vm shim <name> <tool> [exit[:N]|hang|sleep:S]`: a fake tool ahead of the real one on PATH, every call logged with an epoch to /var/log/vmbox-shim/<tool>.log so the host can assert what ran and in what order. `hang` is the openrgb 1.0-2 that never returned on 2026-09-12 and defeated the curfew. - The DISPLAY/XAUTHORITY resolver `vm run` sources becomes its own file, guest/vmbox-x11.sh, installed by the image build and re-installed by the lightdm switch; it now tests readability, not existence, and tries ~/.Xauthority before the startx serverauth glob, because lightdm's Xorg -auth file is root-only. - README: the worked example uses MIDNIGHT_SHUTDOWN_CONFIRM=y (the installer refuses to run unattended now; `echo y |` no longer works), and the "i2c RGB stays on the host" line is gone. - bats: four host-side tests for shim.sh (exit, exit:N, hang, refusals). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015mLnUeoJ3mUwGRTowTUH6d
This commit is contained in:
parent
96f3bb5326
commit
00bb6706fd
@ -71,15 +71,22 @@ the discriminator, and it is asserted by grep, not read by eye.
|
||||
## Worked example: testing the real shutdown installer
|
||||
|
||||
```bash
|
||||
vm share ~/src/testsAndMisc # read-only bind, not a symlink
|
||||
vm share ~/src/utils # guard-lib lives here
|
||||
vm share ~/src/digital-wellbeing # read-only bind, not a symlink
|
||||
vm share ~/src/utils/guard-lib # the config guard it installs
|
||||
vm new st --rtc 2026-08-22T20:55:00 # inside the 21:00-05:00 window
|
||||
vm run st 'git clone --no-hardlinks -q /mnt/hostrepo/testsAndMisc ~/tam'
|
||||
vm run st 'echo y | sudo bash ~/tam/linux_configuration/scripts/periodic_background/digital_wellbeing/setup_midnight_shutdown.sh enable'
|
||||
vm repo st ~/src/digital-wellbeing --worktree
|
||||
vm repo st ~/src/utils/guard-lib --worktree
|
||||
vm run st 'sudo bash ~/guard-lib/install.sh'
|
||||
vm run st 'sudo MIDNIGHT_SHUTDOWN_CONFIRM=y bash ~/digital-wellbeing/setup_midnight_shutdown.sh enable'
|
||||
vm run st 'sudo systemctl poweroff' # -> VERDICT: clean poweroff
|
||||
vm reset st # back to pristine
|
||||
```
|
||||
|
||||
The full night-lockdown flow (lightdm topology, a hanging `openrgb` shim, the
|
||||
per-minute timer, mask/stop, black screen, morning unlock) is scripted in
|
||||
`digital-wellbeing/tests/vmbox_night_lockdown.sh` and is the reference for
|
||||
how `vm lightdm` and `vm shim` fit together.
|
||||
|
||||
Verified: the config lands at `/etc/shutdown-schedule.conf` under guard-lib's
|
||||
immutable protection, and `shutdown-timer-monitor.service`,
|
||||
`day-specific-shutdown.timer` and `shutdown-timer-monitor-watchdog.timer` all
|
||||
@ -88,9 +95,10 @@ timestamp and `/etc/hosts` kept its `+i` flag.
|
||||
|
||||
Two things that installer taught us, both worth knowing before you run it:
|
||||
|
||||
- **It is interactive.** It ends on `read -r -p 'Do you want to proceed?'`.
|
||||
- **It refuses to run unattended.** It ends on a confirmation prompt, and
|
||||
`vm run` closes stdin on purpose (installers that prompt would otherwise
|
||||
hang forever), so pipe `echo y |` when you actually want it to proceed.
|
||||
hang forever). Piping `echo y |` no longer works either -- with no terminal
|
||||
it declines by design. Set `MIDNIGHT_SHUTDOWN_CONFIRM=y` to proceed.
|
||||
- **It is subcommand-driven** (`enable` / `status`). Bare invocation prints
|
||||
usage and exits 1 without changing anything.
|
||||
|
||||
@ -119,14 +127,33 @@ Two things that installer taught us, both worth knowing before you run it:
|
||||
| `vm run <name> <cmd...>` | Run a command, print the verdict |
|
||||
| `vm ssh <name>` | Interactive shell (starts the VM if needed) |
|
||||
| `vm screenshot <name> [out.png]` | Capture the screen (locker/X11 tests) — verified: real 1280x800 PNG of the guest's i3 session, no viewer installed |
|
||||
| `vm lightdm <name>` | Switch the sandbox to the host's login topology: lightdm autologin → i3, tty1 a plain getty. Reboots the guest and proves `lightdm.service` is active with an Xorg it owns |
|
||||
| `vm shim <name> <tool> [exit[:N]\|hang\|sleep:S]` | Put a fake `<tool>` ahead of the real one on PATH (`/usr/local/sbin`). Every call is logged with an epoch to `/var/log/vmbox-shim/<tool>.log`, so the host can assert what ran and in what order. `hang` reproduces a tool that never returns |
|
||||
| `vm reset <name>` | Wipe back to pristine |
|
||||
| `vm rm <name>` / `vm list` | Delete / list sandboxes |
|
||||
|
||||
## Hardware the guest does not have: shim it
|
||||
|
||||
The guest has no RGB controllers, no NVIDIA card and no sound card, and the
|
||||
golden image logs in with agetty+startx rather than a display manager. None
|
||||
of that is a reason to test on the host. `vm lightdm` gives a sandbox the
|
||||
host's login units (the night-lockdown scripts mask/stop `lightdm.service`
|
||||
and `getty@.service`, which do not exist under startx), and `vm shim` stands
|
||||
in for `openrgb`, `nvidia-smi`, `amixer` with a chosen behaviour and a call
|
||||
log. The 2026-09-12 curfew failure -- openrgb 1.0-2 never returning, before
|
||||
the teardown -- is `vm shim nl openrgb hang`, and the fix is proven by the
|
||||
lockdown completing anyway.
|
||||
|
||||
Both mutate a live overlay only. The base image is never rebuilt for this:
|
||||
`vm build --force` re-seals a new checksum while every existing sandbox still
|
||||
backs onto the old file's *name*, which is silent qcow2 corruption, not a
|
||||
loud refusal.
|
||||
|
||||
## Out of scope
|
||||
|
||||
A VM cannot cover everything: adb/phone tests (~36 files), GPU monitors, the
|
||||
i2c RGB in `setup_night_lockdown.sh`, and the Nextcloud/SearXNG suites (whose
|
||||
fixtures assume a Debian layout) all stay on the host or stay untested.
|
||||
A VM still cannot cover adb/phone tests (~36 files), real GPU monitors, and
|
||||
the Nextcloud/SearXNG suites (whose fixtures assume a Debian layout); those
|
||||
stay on the host or stay untested.
|
||||
|
||||
## Tests
|
||||
|
||||
|
||||
@ -28,6 +28,10 @@ Usage: vm <command> [args]
|
||||
repo <name> <path> [--worktree]
|
||||
Copy a host repo in (clone=HEAD, --worktree=uncommitted)
|
||||
screenshot <name> [out.png] Capture the guest screen (for locker tests)
|
||||
lightdm <name> Switch the sandbox to lightdm autologin -> i3,
|
||||
tty1 a plain getty (the host's login topology)
|
||||
shim <name> <tool> [mode] Fake a hardware tool on PATH: exit[:N] | hang |
|
||||
sleep:S; calls logged in /var/log/vmbox-shim/
|
||||
reset <name> Wipe back to pristine (sub-second)
|
||||
rm <name> Delete the sandbox entirely
|
||||
list List sandboxes and their state
|
||||
@ -95,6 +99,8 @@ main() {
|
||||
ssh) source "$VMBOX_LIB_DIR/ssh.sh"; vm_ssh_interactive "$@" ;;
|
||||
scp) source "$VMBOX_LIB_DIR/ssh.sh"; cmd_scp "$@" ;;
|
||||
screenshot) source "$VMBOX_LIB_DIR/screenshot.sh"; vm_screenshot "$@" ;;
|
||||
lightdm) require_host_deps; source "$VMBOX_LIB_DIR/desktop.sh"; cmd_lightdm "$@" ;;
|
||||
shim) require_host_deps; source "$VMBOX_LIB_DIR/desktop.sh"; cmd_shim "$@" ;;
|
||||
repo) source "$VMBOX_LIB_DIR/repo.sh"; cmd_repo "$@" ;;
|
||||
share) source "$VMBOX_LIB_DIR/repo.sh"; repo_share "$@" ;;
|
||||
unshare) source "$VMBOX_LIB_DIR/repo.sh"; repo_unshare "$@" ;;
|
||||
|
||||
69
vmbox/guest/desktop-lightdm.sh
Normal file
69
vmbox/guest/desktop-lightdm.sh
Normal file
@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
# ============================================================================
|
||||
# Runs INSIDE a sandbox as root, via `vm lightdm <name>`.
|
||||
#
|
||||
# Switch the guest's graphical session from the golden image's agetty+startx
|
||||
# autologin to a display manager, mirroring kuhy's real machine: lightdm with
|
||||
# autologin straight into i3, tty1 a plain login. Scripts that mask/stop
|
||||
# lightdm.service and getty@.service (night-lockdown) have nothing to act on
|
||||
# under startx; here they get the same units the host has.
|
||||
#
|
||||
# Applied to a live overlay, never to the base image: `vm build --force`
|
||||
# would silently corrupt every existing sandbox (their backing file changes
|
||||
# underneath them), so the topology change is a per-sandbox step instead.
|
||||
# Takes effect after a reboot, which the host side performs.
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GUEST_USER="${1:-arch}"
|
||||
readonly MARKER="/etc/vmbox-desktop"
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
if [[ -f "$MARKER" && "$(cat "$MARKER")" == "lightdm" ]]; then
|
||||
echo "desktop-lightdm: already converted"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# -Syu, not -S: the overlay's package database is the golden image's, so a
|
||||
# plain -S asks mirrors for versions they have already rotated out (404 on
|
||||
# adwaita-fonts, seen 2026-09-13), and a partial upgrade is unsupported anyway.
|
||||
# The host's pacman cache is mounted read-only, so most of this is local.
|
||||
pacman -Syu --noconfirm --needed lightdm lightdm-gtk-greeter >/dev/null
|
||||
echo "desktop-lightdm: lightdm installed"
|
||||
|
||||
# Same shape as the host's /etc/lightdm/lightdm.conf: autologin with no
|
||||
# greeter delay, into the i3 session that i3-wm ships in /usr/share/xsessions.
|
||||
# Arch's lightdm only honours autologin for members of the `autologin` group.
|
||||
groupadd -r -f autologin
|
||||
gpasswd -a "$GUEST_USER" autologin >/dev/null
|
||||
install -d -m 755 /etc/lightdm/lightdm.conf.d
|
||||
cat > /etc/lightdm/lightdm.conf.d/50-vmbox-autologin.conf <<CONF
|
||||
[Seat:*]
|
||||
greeter-session=lightdm-gtk-greeter
|
||||
user-session=i3
|
||||
autologin-user=${GUEST_USER}
|
||||
autologin-user-timeout=0
|
||||
autologin-session=i3
|
||||
CONF
|
||||
|
||||
# tty1 becomes a plain getty, as on the host: drop the autologin drop-in and
|
||||
# the startx-on-tty1 hook, or two X servers would fight over the seat.
|
||||
rm -f /etc/systemd/system/getty@tty1.service.d/autologin.conf
|
||||
rmdir /etc/systemd/system/getty@tty1.service.d 2>/dev/null || true
|
||||
cat > "/home/$GUEST_USER/.bash_profile" <<'PROFILE'
|
||||
[[ -f ~/.bashrc ]] && . ~/.bashrc
|
||||
PROFILE
|
||||
chown "$GUEST_USER:$GUEST_USER" "/home/$GUEST_USER/.bash_profile"
|
||||
|
||||
# lightdm starts Xorg with -auth /run/lightdm/root/:0, readable by root only;
|
||||
# the user's session cookie is ~/.Xauthority. Re-install the DISPLAY resolver
|
||||
# `vm run` sources from its single source, which already prefers whichever
|
||||
# auth file is readable -- older golden images carry a version that does not.
|
||||
install -m 644 "$HERE/vmbox-x11.sh" /etc/profile.d/vmbox-x11.sh
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable lightdm.service >/dev/null 2>&1
|
||||
systemctl set-default graphical.target >/dev/null 2>&1
|
||||
echo lightdm > "$MARKER"
|
||||
echo "desktop-lightdm: lightdm.service enabled, autologin ${GUEST_USER} -> i3 (reboot to apply)"
|
||||
61
vmbox/guest/provision-desktop.sh
Executable file → Normal file
61
vmbox/guest/provision-desktop.sh
Executable file → Normal file
@ -49,61 +49,12 @@ I3
|
||||
chown -R "$GUEST_USER:$GUEST_USER" "/home/$GUEST_USER/.config"
|
||||
|
||||
# --- X session discovery for non-login commands ---------------------------
|
||||
# The image ships a real X/i3 session on tty1 so locker and i3 tests can run.
|
||||
# But `vm run` executes through `sh -c`, which reads no profile, so those
|
||||
# The image ships a real X/i3 session so locker and i3 tests can run. But
|
||||
# `vm run` executes through `sh -c`, which reads no profile, so those
|
||||
# commands land with no DISPLAY and no XAUTHORITY -- and every X tool then
|
||||
# fails with "Could not determine i3 socket path" / "cannot open display".
|
||||
# That looks exactly like "the sandbox has no X", which is wrong and cost a
|
||||
# real installer run to diagnose.
|
||||
#
|
||||
# XAUTHORITY is the awkward half: startx generates /tmp/serverauth.XXXXXXXX
|
||||
# with a random suffix on every boot, so it cannot be hardcoded. Resolve it
|
||||
# from the running Xorg process instead, falling back to a glob.
|
||||
cat > /etc/profile.d/vmbox-x11.sh <<'XENV'
|
||||
# Point non-login shells at the guest's X session, if one is running.
|
||||
# Sourced by vmbox's `vm run`; harmless when no X session exists.
|
||||
if [ -z "${DISPLAY:-}" ]; then
|
||||
# Wait briefly for the X session rather than reporting "no X".
|
||||
#
|
||||
# vmbox now returns control as soon as sshd answers (~11s), which is
|
||||
# EARLIER than getty@tty1 autologin has run .bash_profile and started
|
||||
# Xorg. Without this wait the first `vm run` after a boot silently gets
|
||||
# no DISPLAY and every X tool fails as if the sandbox had no X server --
|
||||
# the exact failure this file exists to prevent, reintroduced by making
|
||||
# the rest of the tool faster. Bounded, and skipped entirely once Xorg
|
||||
# is up, so a guest with no graphical session costs at most this wait.
|
||||
# Default 0: a plain `vm run` must not pay for a graphical session it is
|
||||
# not using. Set VMBOX_X_WAIT=<seconds> for X11/i3/locker work.
|
||||
_vmbox_x_wait="${VMBOX_X_WAIT:-0}"
|
||||
while [ "$_vmbox_x_wait" -gt 0 ]; do
|
||||
pgrep -x Xorg >/dev/null 2>&1 && break
|
||||
sleep 1
|
||||
_vmbox_x_wait=$((_vmbox_x_wait - 1))
|
||||
done
|
||||
unset _vmbox_x_wait
|
||||
if pgrep -x Xorg >/dev/null 2>&1; then
|
||||
DISPLAY=":0"
|
||||
export DISPLAY
|
||||
fi
|
||||
fi
|
||||
if [ -n "${DISPLAY:-}" ] && [ -z "${XAUTHORITY:-}" ]; then
|
||||
# Prefer the auth file the running Xorg was actually started with.
|
||||
_vmbox_xauth="$(tr '\0' '\n' < /proc/"$(pgrep -x Xorg | head -1)"/cmdline 2>/dev/null \
|
||||
| grep -A1 -x -- -auth | tail -1)"
|
||||
if [ ! -f "${_vmbox_xauth:-}" ]; then
|
||||
_vmbox_xauth="$(ls -1t /tmp/serverauth.* 2>/dev/null | head -1)"
|
||||
fi
|
||||
if [ -f "${_vmbox_xauth:-}" ]; then
|
||||
XAUTHORITY="$_vmbox_xauth"
|
||||
export XAUTHORITY
|
||||
fi
|
||||
unset _vmbox_xauth
|
||||
fi
|
||||
# i3 tools look here when I3SOCK is unset; setting it explicitly avoids a
|
||||
# second round of X round-trips in tests that shell out repeatedly.
|
||||
if [ -n "${DISPLAY:-}" ] && [ -z "${I3SOCK:-}" ] && command -v i3 >/dev/null 2>&1; then
|
||||
I3SOCK="$(i3 --get-socketpath 2>/dev/null || true)"
|
||||
[ -n "$I3SOCK" ] && export I3SOCK || unset I3SOCK
|
||||
fi
|
||||
XENV
|
||||
chmod 644 /etc/profile.d/vmbox-x11.sh
|
||||
# real installer run to diagnose. The resolver is its own file, guest/
|
||||
# vmbox-x11.sh, because desktop-lightdm.sh re-installs it after switching a
|
||||
# sandbox to lightdm (different auth-file location) -- one source for both.
|
||||
install -m 644 "$(dirname "${BASH_SOURCE[0]}")/vmbox-x11.sh" /etc/profile.d/vmbox-x11.sh
|
||||
|
||||
49
vmbox/guest/shim.sh
Normal file
49
vmbox/guest/shim.sh
Normal file
@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
# ============================================================================
|
||||
# Runs INSIDE a sandbox as root, via `vm shim <name> <tool> [mode]`.
|
||||
#
|
||||
# Install a fake <tool> ahead of the real one on PATH (/usr/local/sbin is
|
||||
# first for root and for systemd services), so scripts that shell out to
|
||||
# hardware the guest does not have -- openrgb, nvidia-smi, amixer -- can be
|
||||
# driven through a chosen behaviour instead of "command not found":
|
||||
#
|
||||
# exit[:N] return N immediately (default 0)
|
||||
# hang never return (what openrgb 1.0-2 did on 2026-09-12)
|
||||
# sleep:S return 0 after S seconds
|
||||
#
|
||||
# Every invocation is appended to /var/log/vmbox-shim/<tool>.log as
|
||||
# "<epoch> <argv>", so the host can assert from the guest what was called,
|
||||
# with which arguments, and in what order relative to other shims.
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
TOOL="${1:?usage: shim.sh <tool> [exit[:N]|hang|sleep:S]}"
|
||||
MODE="${2:-exit}"
|
||||
# Overridable for the host-side bats tests, which must not write to /usr.
|
||||
readonly SHIM_DIR="${VMBOX_SHIM_DIR:-/usr/local/sbin}"
|
||||
readonly LOG_DIR="${VMBOX_SHIM_LOG_DIR:-/var/log/vmbox-shim}"
|
||||
|
||||
case "$TOOL" in
|
||||
*/*|'') echo "shim: tool must be a bare command name, got '$TOOL'" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
behaviour=""
|
||||
case "$MODE" in
|
||||
exit) behaviour='exit 0' ;;
|
||||
exit:*) behaviour="exit ${MODE#exit:}" ;;
|
||||
hang) behaviour='exec sleep infinity' ;;
|
||||
sleep:*) behaviour="sleep ${MODE#sleep:}; exit 0" ;;
|
||||
*) echo "shim: unknown mode '$MODE' (exit[:N] | hang | sleep:S)" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
install -d -m 755 "$LOG_DIR" "$SHIM_DIR"
|
||||
cat > "$SHIM_DIR/$TOOL" <<SHIM
|
||||
#!/bin/bash
|
||||
# vmbox shim for ${TOOL} (mode: ${MODE}) -- installed by \`vm shim\`.
|
||||
printf '%(%s)T %s\\n' -1 "\$*" >> "${LOG_DIR}/${TOOL}.log"
|
||||
${behaviour}
|
||||
SHIM
|
||||
chmod 755 "$SHIM_DIR/$TOOL"
|
||||
: > "$LOG_DIR/$TOOL.log"
|
||||
echo "shim: $SHIM_DIR/$TOOL installed (mode: $MODE), log: $LOG_DIR/$TOOL.log"
|
||||
59
vmbox/guest/vmbox-x11.sh
Normal file
59
vmbox/guest/vmbox-x11.sh
Normal file
@ -0,0 +1,59 @@
|
||||
#!/bin/sh
|
||||
# Installed to /etc/profile.d/vmbox-x11.sh by provision-desktop.sh (golden
|
||||
# image) and re-installed by desktop-lightdm.sh (per-sandbox lightdm switch).
|
||||
# Sourced by `vm run`; POSIX sh, because /etc/profile.d may be read by dash.
|
||||
#
|
||||
# Point non-login shells at the guest's X session, if one is running.
|
||||
# Sourced by vmbox's `vm run`; harmless when no X session exists.
|
||||
if [ -z "${DISPLAY:-}" ]; then
|
||||
# Wait briefly for the X session rather than reporting "no X".
|
||||
#
|
||||
# vmbox now returns control as soon as sshd answers (~11s), which is
|
||||
# EARLIER than getty@tty1 autologin has run .bash_profile and started
|
||||
# Xorg. Without this wait the first `vm run` after a boot silently gets
|
||||
# no DISPLAY and every X tool fails as if the sandbox had no X server --
|
||||
# the exact failure this file exists to prevent, reintroduced by making
|
||||
# the rest of the tool faster. Bounded, and skipped entirely once Xorg
|
||||
# is up, so a guest with no graphical session costs at most this wait.
|
||||
# Default 0: a plain `vm run` must not pay for a graphical session it is
|
||||
# not using. Set VMBOX_X_WAIT=<seconds> for X11/i3/locker work.
|
||||
_vmbox_x_wait="${VMBOX_X_WAIT:-0}"
|
||||
while [ "$_vmbox_x_wait" -gt 0 ]; do
|
||||
pgrep -x Xorg >/dev/null 2>&1 && break
|
||||
sleep 1
|
||||
_vmbox_x_wait=$((_vmbox_x_wait - 1))
|
||||
done
|
||||
unset _vmbox_x_wait
|
||||
if pgrep -x Xorg >/dev/null 2>&1; then
|
||||
DISPLAY=":0"
|
||||
export DISPLAY
|
||||
fi
|
||||
fi
|
||||
if [ -n "${DISPLAY:-}" ] && [ -z "${XAUTHORITY:-}" ]; then
|
||||
# Prefer the auth file the running Xorg was actually started with. Test
|
||||
# READABILITY, not existence: under lightdm that is /run/lightdm/root/:0,
|
||||
# root-only, and the user's cookie is ~/.Xauthority instead. The startx
|
||||
# serverauth glob is the last resort for the golden image's tty1 session.
|
||||
_vmbox_xauth="$(tr '\0' '\n' < /proc/"$(pgrep -x Xorg | head -1)"/cmdline 2>/dev/null \
|
||||
| grep -A1 -x -- -auth | tail -1)"
|
||||
if [ ! -r "${_vmbox_xauth:-}" ]; then
|
||||
_vmbox_xauth="$HOME/.Xauthority"
|
||||
fi
|
||||
if [ ! -r "${_vmbox_xauth:-}" ]; then
|
||||
for _vmbox_cand in /tmp/serverauth.*; do
|
||||
[ -r "$_vmbox_cand" ] && _vmbox_xauth="$_vmbox_cand"
|
||||
done
|
||||
unset _vmbox_cand
|
||||
fi
|
||||
if [ -r "${_vmbox_xauth:-}" ]; then
|
||||
XAUTHORITY="$_vmbox_xauth"
|
||||
export XAUTHORITY
|
||||
fi
|
||||
unset _vmbox_xauth
|
||||
fi
|
||||
# i3 tools look here when I3SOCK is unset; setting it explicitly avoids a
|
||||
# second round of X round-trips in tests that shell out repeatedly.
|
||||
if [ -n "${DISPLAY:-}" ] && [ -z "${I3SOCK:-}" ] && command -v i3 >/dev/null 2>&1; then
|
||||
I3SOCK="$(i3 --get-socketpath 2>/dev/null || true)"
|
||||
if [ -n "$I3SOCK" ]; then export I3SOCK; else unset I3SOCK; fi
|
||||
fi
|
||||
@ -114,9 +114,11 @@ base_provision() {
|
||||
|
||||
phase_begin "Provisioning guest (installs Xorg/i3 + dev tools)"
|
||||
# provision.sh sources a sibling (provision-desktop.sh) by path, so BOTH
|
||||
# must land in the same directory in the guest -- copying only the entry
|
||||
# must land in the same directory in the guest (and provision-desktop.sh
|
||||
# installs vmbox-x11.sh from beside itself) -- copying only the entry
|
||||
# point produces a build that dies partway with "No such file or directory".
|
||||
_base_scp "$VMBOX_GUEST_DIR/provision.sh" "/tmp/provision.sh"
|
||||
_base_scp "$VMBOX_GUEST_DIR/vmbox-x11.sh" "/tmp/vmbox-x11.sh"
|
||||
_base_scp "$VMBOX_GUEST_DIR/provision-desktop.sh" "/tmp/provision-desktop.sh"
|
||||
_base_ssh "chmod +x /tmp/provision.sh /tmp/provision-desktop.sh && sudo /tmp/provision.sh '$VMBOX_GUEST_USER'" ||
|
||||
die "provisioning failed -- see $serial"
|
||||
|
||||
88
vmbox/lib/desktop.sh
Normal file
88
vmbox/lib/desktop.sh
Normal file
@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
# ============================================================================
|
||||
# vmbox: make a sandbox look like the host where the base image cannot.
|
||||
#
|
||||
# vm lightdm <name> lightdm autologin -> i3, tty1 a plain getty
|
||||
# vm shim <name> <tool> [mode] fake a hardware tool (openrgb, nvidia-smi)
|
||||
#
|
||||
# Both mutate a live overlay and are undone by `vm reset`. Neither touches the
|
||||
# golden image: rebuilding it would silently corrupt every existing sandbox.
|
||||
# ============================================================================
|
||||
|
||||
# shellcheck source=common.sh
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/common.sh"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/ssh.sh"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/launch.sh"
|
||||
|
||||
readonly DESKTOP_REBOOT_SSH_TIMEOUT="${VMBOX_DESKTOP_REBOOT_SSH_TIMEOUT:-150}"
|
||||
|
||||
_desktop_ensure_running() {
|
||||
local name="$1"
|
||||
if ! vm_is_running "$name"; then
|
||||
launch_vm "$name" >/dev/null
|
||||
vm_wait_ssh "$name" 150 || die "sandbox '$name' did not become reachable"
|
||||
fi
|
||||
}
|
||||
|
||||
# Copy a guest/ script (plus any siblings it needs) in and run it as root.
|
||||
_desktop_run_guest_script() {
|
||||
local name="$1" script="$2"; shift 2
|
||||
local f
|
||||
for f in "$script" "$@"; do
|
||||
vm_scp_to "$name" "$VMBOX_GUEST_DIR/$f" "/tmp/$f" ||
|
||||
die "could not copy $f into '$name'"
|
||||
done
|
||||
vm_ssh_exec "$name" "chmod +x /tmp/$script && sudo /tmp/$script $VMBOX_GUEST_USER"
|
||||
}
|
||||
|
||||
# vm lightdm <name>
|
||||
cmd_lightdm() {
|
||||
local name
|
||||
name="$(validate_vm_name "${1:-}")"
|
||||
require_vm "$name"
|
||||
_desktop_ensure_running "$name"
|
||||
|
||||
log "Switching '$name' to lightdm autologin -> i3 (host topology)"
|
||||
_desktop_run_guest_script "$name" desktop-lightdm.sh vmbox-x11.sh ||
|
||||
die "lightdm switch failed in '$name'"
|
||||
|
||||
# The switch only takes effect on a fresh boot: lightdm must own the seat
|
||||
# from the start, and the startx session on tty1 must never have run.
|
||||
# -no-reboot makes a guest reboot exit qemu, so relaunch it ourselves.
|
||||
log "Rebooting '$name' to bring lightdm up"
|
||||
vm_ssh_exec "$name" "sudo systemctl reboot" >/dev/null 2>&1 || true
|
||||
local waited=0
|
||||
while vm_is_running "$name" && (( waited < 60 )); do
|
||||
sleep 1; waited=$(( waited + 1 ))
|
||||
done
|
||||
vm_is_running "$name" && die "'$name' did not go down for the reboot"
|
||||
launch_vm "$name" >/dev/null
|
||||
vm_wait_ssh "$name" "$DESKTOP_REBOOT_SSH_TIMEOUT" ||
|
||||
die "'$name' did not come back after the reboot"
|
||||
|
||||
# Prove the topology from the guest, not from the exit code: the unit is
|
||||
# active AND an Xorg it owns is up -- that is what a lockdown script sees.
|
||||
local state
|
||||
state="$(vm_ssh_exec "$name" "export VMBOX_X_WAIT=45; . /etc/profile.d/vmbox-x11.sh; \
|
||||
printf '%s %s %s' \"\$(systemctl is-active lightdm.service)\" \
|
||||
\"\$(systemctl is-enabled getty@tty1.service 2>/dev/null || echo -)\" \"\${DISPLAY:-none}\"" 2>/dev/null)"
|
||||
[[ "$state" == "active "*" :0" ]] ||
|
||||
die "lightdm did not come up cleanly in '$name' (lightdm/getty@tty1/DISPLAY: $state)"
|
||||
ok "'$name': lightdm.service active, autologin i3 session on ${state##* }"
|
||||
}
|
||||
|
||||
# vm shim <name> <tool> [exit[:N]|hang|sleep:S]
|
||||
cmd_shim() {
|
||||
local name tool mode
|
||||
name="$(validate_vm_name "${1:-}")"
|
||||
tool="${2:-}"; mode="${3:-exit}"
|
||||
[[ -n "$tool" ]] || die "usage: vm shim <name> <tool> [exit[:N]|hang|sleep:S]"
|
||||
require_vm "$name"
|
||||
_desktop_ensure_running "$name"
|
||||
|
||||
vm_scp_to "$name" "$VMBOX_GUEST_DIR/shim.sh" "/tmp/shim.sh" ||
|
||||
die "could not copy shim.sh into '$name'"
|
||||
vm_ssh_exec "$name" "chmod +x /tmp/shim.sh && sudo /tmp/shim.sh '$tool' '$mode'" ||
|
||||
die "shim install failed in '$name'"
|
||||
ok "'$name': $tool is now a shim ($mode); calls logged to /var/log/vmbox-shim/$tool.log"
|
||||
}
|
||||
@ -389,3 +389,41 @@ _verdict_case() {
|
||||
[ "$status" -eq 6 ]
|
||||
[[ "$output" == *"unrecognised"* ]]
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# guest/shim.sh -- fake hardware tools (runs on the host against a temp PATH)
|
||||
# ---------------------------------------------------------------------------
|
||||
_shim() {
|
||||
VMBOX_SHIM_DIR="$VMBOX_TEST_HOME/sbin" VMBOX_SHIM_LOG_DIR="$VMBOX_TEST_HOME/shimlog" \
|
||||
bash "$REPO_ROOT/guest/shim.sh" "$@"
|
||||
}
|
||||
|
||||
@test "shim: exit mode returns 0 and logs the argv with an epoch" {
|
||||
run _shim openrgb exit
|
||||
[ "$status" -eq 0 ]
|
||||
run "$VMBOX_TEST_HOME/sbin/openrgb" --mode static --color 000000
|
||||
[ "$status" -eq 0 ]
|
||||
run cat "$VMBOX_TEST_HOME/shimlog/openrgb.log"
|
||||
[[ "$output" =~ ^[0-9]{9,}\ --mode\ static\ --color\ 000000$ ]]
|
||||
}
|
||||
|
||||
@test "shim: exit:N returns N" {
|
||||
_shim nvidia-smi exit:3
|
||||
run "$VMBOX_TEST_HOME/sbin/nvidia-smi" -pl 100
|
||||
[ "$status" -eq 3 ]
|
||||
}
|
||||
|
||||
@test "shim: hang mode never returns (the 2026-09-12 openrgb)" {
|
||||
_shim openrgb hang
|
||||
run timeout 1 "$VMBOX_TEST_HOME/sbin/openrgb"
|
||||
[ "$status" -eq 124 ]
|
||||
run cat "$VMBOX_TEST_HOME/shimlog/openrgb.log"
|
||||
[ "${#lines[@]}" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "shim: an unknown mode or a path is refused" {
|
||||
run _shim openrgb explode
|
||||
[ "$status" -eq 1 ]
|
||||
run _shim /usr/bin/openrgb exit
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user