From 4b0e0135b3fea7d7df964141fcabc300a622b4b3 Mon Sep 17 00:00:00 2001 From: Audiobuzz Date: Wed, 17 Jun 2026 13:20:52 +0000 Subject: [PATCH] Fix kiosk mode --- diskless.pkr.hcl | 2 +- docs/TODO.md | 54 ++++++++++++++++++++++------------ files/kiosk/kiosk-session | 61 ++++++++++++++++++++++++++++----------- files/kiosk/kiosk.service | 24 ++++++++------- files/kiosk/weston.ini | 25 ++++++++++------ 5 files changed, 111 insertions(+), 55 deletions(-) diff --git a/diskless.pkr.hcl b/diskless.pkr.hcl index 9d06e68..830f810 100644 --- a/diskless.pkr.hcl +++ b/diskless.pkr.hcl @@ -115,7 +115,7 @@ variable "admin_user" { variable "enable_kiosk" { type = bool description = "Build-time feature flag for the graphical kiosk (weston + Chromium + mesa). false (default, current) skips the kiosk provisioner entirely — much faster builds and a far smaller image — while iterating on the boot/network stack. Set true to bake the kiosk back in. (kiosk_url=\"\" also skips it; this flag lets you disable without losing the configured URL.)" - default = false + default = true } variable "kiosk_url" { diff --git a/docs/TODO.md b/docs/TODO.md index 28b7504..a94a513 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -83,11 +83,29 @@ step 8 and §5). Retire/reset the errata afterward. Needs the design-doc source ## [~] 4. Kiosk mode — autologin + fullscreen Chromium loading a URL -**First cut implemented (2026-06-16) — needs a display to verify.** Wayland -kiosk + Chromium from EPEL, baked into the root. **cage is NOT packaged for EL9** -(`dnf install cage` → "Unable to find a match"), so switched to **weston -kiosk-shell** (weston 8.0.0-10.el9 is in EPEL; kiosk-shell.so landed in weston -8.0; far lighter than the GDM+gnome-kiosk alternative). +**WORKING / display-verified 2026-06-17** (kiosk renders Chromium fullscreen on +the configured URL). Build-flagged OFF by default for now (`enable_kiosk=false`) +while iterating on boot/network — set `-var enable_kiosk=true` to ship it. + +Path to getting here (don't re-discover): +- **cage is NOT packaged for EL9** (`dnf install cage` → "Unable to find a + match"). Tried weston instead. +- **EPEL 9 ships weston 8.0**, which has NEITHER `kiosk-shell.so` NOR the + `[autolaunch]` weston.ini section — both arrived in **weston 9.0**. The first + cut wrongly assumed 8.0 had them; `shell=kiosk-shell.so` failed to load. + `rpm -ql weston` shows only desktop-shell / fullscreen-shell / ivi-shell. +- **Resolution: `desktop-shell.so`** (the only packaged shell Chromium's + xdg-shell works with — fullscreen-shell needs a client protocol Chromium + doesn't speak), stripped bare (`panel-position=none`, black bg, no + animation/lock), and Chromium launched by a **wrapper** (kiosk-session) since + there's no `[autolaunch]`. Chromium `--kiosk` fullscreens over the shell. +- **Seat fix (load-bearing):** weston died with `logind: failed to get session + seat` → `failed to create compositor backend`. logind's auto-derivation + didn't attach a seat; `Environment=XDG_SEAT=seat0` + `XDG_VTNR=1` in the unit + (put into the PAM env before pam_open_session) registers the session on + seat0/vt1. `/dev/dri/card0` was present all along (not the issue). +- Tested live without a rebuild: the whole root is `systemd.volatile=overlay`, + so /etc AND /usr are writable tmpfs at runtime — drop-in the files + restart. - **`enable_kiosk` Packer var — currently `false` by DEFAULT (set 2026-06-17).** The whole kiosk provisioner is feature-flagged off while iterating on the boot/network stack (DHCP-lease/keep-alive work), for faster builds and a @@ -99,19 +117,19 @@ kiosk-shell** (weston 8.0.0-10.el9 is in EPEL; kiosk-shell.so landed in weston - Provisioner enables EPEL, installs `weston chromium mesa-dri-drivers mesa-libEGL mesa-libgbm dejavu-sans-fonts`, creates a passwordless unprivileged `kiosk` user (video/input groups). -- `files/kiosk/kiosk.service` — runs `weston --use-pixman - --config=/etc/xdg/weston/weston.ini` as `kiosk` on tty1 via - `PAMName=login`+`TTYPath` (gets seat0 for DRM/input), `Conflicts=getty@tty1`, - `WantedBy=graphical.target`; default target set to graphical. -- `files/kiosk/weston.ini` — `shell=kiosk-shell.so`, `idle-time=0`, - `require-input=false`, `xwayland=false`, `[autolaunch] path=/usr/local/bin/ - kiosk-session watch=true`. -- `files/kiosk/kiosk-session` — Chromium with `--kiosk --ozone-platform=wayland - --disable-gpu` + tmpfs profile/cache; URL from `/etc/kiosk/kiosk.env`. -- **Verify on a VM with a console (Proxmox VNC/SPICE):** weston comes up on - tty1, Chromium loads the URL over the booted network. Likely tweak points: - Chromium ozone/wayland support, dropping `--disable-gpu` on real GPUs, - `/dev/dri` present in the VM (weston needs a DRM device — set Display to +- `files/kiosk/kiosk.service` — runs `/usr/local/bin/kiosk-session` as `kiosk` + on tty1 via `PAMName=login`+`TTYPath`+`Environment=XDG_SEAT=seat0`/`XDG_VTNR=1` + (gets seat0 for DRM/input), `Conflicts=getty@tty1`, `WantedBy=graphical.target`; + default target set to graphical. +- `files/kiosk/weston.ini` — `shell=desktop-shell.so`, `idle-time=0`, + `require-input=false`, `xwayland=false`; `[shell] panel-position=none`, + black `background-color`, `animation=none`, `locking=false`. +- `files/kiosk/kiosk-session` — `exec`s `weston --use-pixman --socket=wayland-kiosk` + in the foreground (owns the seat), and launches Chromium (`--kiosk + --ozone-platform=wayland --disable-gpu`, tmpfs profile/cache, URL from + `/etc/kiosk/kiosk.env`) in a background crash-restart loop. +- **Remaining polish (now that it renders):** dropping `--disable-gpu` on real + GPUs, `/dev/dri` present in the VM (weston needs a DRM device — set Display to VirtIO-GPU/std), and **SELinux AVCs** for the GUI stack (check `ausearch -m avc`; the kiosk user is unconfined so likely none — but do NOT set permissive if they appear; add a policy module). Admin SSH (the key user) still works for diff --git a/files/kiosk/kiosk-session b/files/kiosk/kiosk-session index 2b84fcb..a17688a 100644 --- a/files/kiosk/kiosk-session +++ b/files/kiosk/kiosk-session @@ -1,22 +1,49 @@ #!/bin/bash -# kiosk-session — launched by cage (Wayland) from kiosk.service. -# Starts Chromium fullscreen on the configured URL. +# kiosk-session — main process of kiosk.service. # -# KIOSK_URL comes from /etc/kiosk/kiosk.env (baked from the kiosk_url Packer -# var; a runtime config-agent could override it later). The Chromium profile is -# pinned to a tmpfs path so writes land in RAM (the root is read-only erofs + -# volatile overlay) — watch RAM if the page is heavy. +# Launches the weston compositor in the FOREGROUND (so it owns the logind seat +# session systemd set up via PAMName=login) and, once weston's Wayland socket is +# up, runs Chromium fullscreen in the BACKGROUND with a crash-restart loop. # -# VM/GPU notes: --disable-gpu + the pixman wlroots renderer (WLR_RENDERER in -# kiosk.env) keep this working on VMs with no real GPU. On hardware with a GPU, -# drop --disable-gpu (and set WLR_RENDERER=gles2) for compositing. -set -euo pipefail +# Why a wrapper instead of weston.ini [autolaunch]: EPEL 9 ships weston 8.0, +# which has neither kiosk-shell.so nor [autolaunch] (both weston 9.0). So weston +# runs desktop-shell.so (see weston.ini) and we start Chromium here. +# +# Process model: systemd -> this script -> exec weston. The Chromium loop is +# forked before the exec, so it lives in the unit's cgroup; when weston exits the +# service is torn down (KillMode=control-group) and the loop dies with it. If +# Chromium alone crashes, the loop relaunches it without taking weston down. +# +# KIOSK_URL comes from /etc/kiosk/kiosk.env. The Chromium profile/cache live on +# tmpfs (root is read-only erofs + volatile overlay) — writes land in RAM; watch +# RAM if the page is heavy (it can starve the NVMe-oF keep-alive — see TODO). +set -uo pipefail -BROWSER=$(command -v chromium-browser || command -v chromium) +: "${KIOSK_URL:?KIOSK_URL not set in /etc/kiosk/kiosk.env}" +export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" +SOCK=wayland-kiosk -exec "$BROWSER" \ - --kiosk --noerrdialogs --disable-infobars --incognito --no-first-run \ - --disable-session-crashed-bubble --start-maximized \ - --ozone-platform=wayland --disable-gpu \ - --user-data-dir=/tmp/kiosk-profile --disk-cache-dir=/tmp/kiosk-cache \ - "${KIOSK_URL:?KIOSK_URL not set in /etc/kiosk/kiosk.env}" +BROWSER=$(command -v chromium-browser || command -v chromium || true) +: "${BROWSER:?chromium not found (expected chromium-browser or chromium in PATH)}" + +# Background: wait (up to ~60s) for weston's socket, then keep Chromium running. +( + for _ in $(seq 1 120); do + [ -S "$XDG_RUNTIME_DIR/$SOCK" ] && break + sleep 0.5 + done + export WAYLAND_DISPLAY="$SOCK" + while :; do + "$BROWSER" \ + --kiosk --noerrdialogs --disable-infobars --incognito --no-first-run \ + --disable-session-crashed-bubble --start-maximized \ + --ozone-platform=wayland --disable-gpu \ + --user-data-dir=/tmp/kiosk-profile --disk-cache-dir=/tmp/kiosk-cache \ + "$KIOSK_URL" || true + sleep 2 + done +) & + +# weston as the foreground process (owns the seat; service restarts if it dies). +# desktop-shell + explicit socket; --use-pixman = software rendering for VMs. +exec weston --use-pixman --socket="$SOCK" --config=/etc/xdg/weston/weston.ini diff --git a/files/kiosk/kiosk.service b/files/kiosk/kiosk.service index 229e8cb..953fa82 100644 --- a/files/kiosk/kiosk.service +++ b/files/kiosk/kiosk.service @@ -1,5 +1,5 @@ [Unit] -Description=Wayland kiosk (weston kiosk-shell + Chromium) +Description=Wayland kiosk (weston desktop-shell + Chromium) Documentation=man:weston(1) man:weston.ini(5) # Take over tty1 from the login getty and start after the network is up (the # kiosk loads a URL). plymouth-quit lets the splash hand off cleanly. @@ -10,10 +10,15 @@ Conflicts=getty@tty1.service [Service] Type=simple User=kiosk -# A real PAM login session on tty1 so logind assigns seat0 — cage/wlroots needs -# a seat for DRM master + libinput. (PAMName runs account/session, not auth, so -# the passwordless kiosk user is fine.) +# A real PAM login session on tty1 so logind assigns seat0 — weston needs a seat +# for DRM master + libinput. (PAMName runs account/session, not auth, so the +# passwordless kiosk user is fine.) XDG_SEAT/XDG_VTNR are set into the PAM +# environment before pam_open_session, so pam_systemd registers the session on +# seat0/vt1 — WITHOUT them logind's auto-derivation failed and weston died with +# "logind: failed to get session seat" -> "failed to create compositor backend". PAMName=login +Environment=XDG_SEAT=seat0 +Environment=XDG_VTNR=1 TTYPath=/dev/tty1 TTYReset=yes TTYVHangup=yes @@ -24,12 +29,11 @@ StandardError=journal UtmpIdentifier=tty1 UtmpMode=user EnvironmentFile=/etc/kiosk/kiosk.env -# weston with kiosk-shell (every app fullscreen). --use-pixman = software -# rendering for VMs/no-GPU. weston.ini's [autolaunch] runs /usr/local/bin/ -# kiosk-session (Chromium) and, with watch=true, weston exits if it dies — so -# Restart=always relaunches the whole stack. cage (the original choice) isn't -# packaged for EL9; weston kiosk-shell is the minimal EL9-available equivalent. -ExecStart=/usr/bin/weston --use-pixman --config=/etc/xdg/weston/weston.ini +# kiosk-session launches weston (foreground, owns the seat) + Chromium. weston +# 8.0 (EPEL 9) lacks kiosk-shell.so/[autolaunch], so the wrapper does what those +# would have — see kiosk-session and weston.ini. If weston dies, Restart=always +# relaunches the whole stack; a Chromium-only crash is handled inside the wrapper. +ExecStart=/usr/local/bin/kiosk-session Restart=always RestartSec=2 diff --git a/files/kiosk/weston.ini b/files/kiosk/weston.ini index f92890a..2af8422 100644 --- a/files/kiosk/weston.ini +++ b/files/kiosk/weston.ini @@ -1,12 +1,15 @@ -# weston kiosk configuration (consumed by kiosk.service: weston --config=...). +# weston kiosk configuration (consumed by kiosk-session: weston --config=...). # -# kiosk-shell makes every client fullscreen with no decorations/desktop — the -# kiosk behaviour cage gave us. The [autolaunch] app is the Chromium wrapper; -# watch=true makes weston exit when it exits, so kiosk.service (Restart=always) -# relaunches the whole stack on a browser crash. +# Target is EPEL 9's weston 8.0, which has NEITHER kiosk-shell.so NOR the +# [autolaunch] section (both arrived in weston 9.0). So we use desktop-shell.so — +# the only packaged shell that Chromium's xdg-shell surfaces work with — stripped +# down to a bare black surface, and launch Chromium ourselves from kiosk-session. +# Chromium --kiosk then fullscreens over this shell. [core] -shell=kiosk-shell.so +# desktop-shell.so is the shell; fullscreen-shell needs a special client protocol +# Chromium doesn't speak, and kiosk-shell isn't in this weston. +shell=desktop-shell.so # No screen blanking on a kiosk. idle-time=0 # Don't refuse to start if no input device is present (headless/VM consoles). @@ -14,6 +17,10 @@ require-input=false # No X11 apps here — Chromium runs native Wayland (--ozone-platform=wayland). xwayland=false -[autolaunch] -path=/usr/local/bin/kiosk-session -watch=true +[shell] +# Bare kiosk surface: no panel, solid black background, no animations, no lock. +# Chromium --kiosk draws fullscreen on top of this. +panel-position=none +background-color=0xff000000 +animation=none +locking=false