#!/bin/sh # ~/.local/bin/gnome-kiosk-script — the mirror kiosk application. # # Installed by docs/kiosk-silverblue/install-kiosk.sh; see that runbook. # # `gnome-kiosk-script-session` runs this file as the kiosk user inside the # kiosk session, and this script re-execs itself forever (the upstream example # does the same). That has two consequences worth designing around: # # 1. Never launch unconditionally. The loop comes back every few seconds, so # an unguarded launch opens a new window every few seconds. # 2. Never exit. `org.gnome.Kiosk.Script.service` has # `OnSuccess=gnome-session-shutdown.target`, so leaving this script logs # the kiosk out and the screen goes back to the GDM greeter. # # So: poll, (re)launch only when firefox is gone, and keep the loop alive. # Source the per-client config FIRST, then derive everything from it, so the # payload file can stay byte-identical on every client and the kiosk.conf under # ~/.config/mirror is the only thing that differs. Deriving before the source # is a real trap: the script silently runs on the built-in default URL forever. CONF="${MIRROR_KIOSK_CONF:-$HOME/.config/mirror/kiosk.conf}" if [ -r "$CONF" ]; then # shellcheck source=/dev/null . "$CONF" fi URL="${MIRROR_KIOSK_URL:-https://mirror.reeseapps.com/kiosk}" PROFILE="${MIRROR_KIOSK_PROFILE:-$HOME/.mirror-kiosk}" LOG="${MIRROR_KIOSK_LOG:-${XDG_STATE_HOME:-$HOME/.local/state}/mirror-kiosk.log}" POLL="${MIRROR_KIOSK_POLL:-5}" log() { [ -n "$LOG" ] || return 0 # Cap the log: this script runs forever on a machine nobody logs into. if [ -f "$LOG" ] && [ "$(stat -c %s "$LOG" 2>/dev/null || echo 0)" -gt 1048576 ]; then tail -c 65536 "$LOG" >"$LOG.tmp" 2>/dev/null && mv -f "$LOG.tmp" "$LOG" 2>/dev/null fi printf '%s %s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z' 2>/dev/null || echo -)" "$*" >>"$LOG" 2>/dev/null || : } # Keep the screen on. This has to happen here, inside the session, and not at # install time: gsettings only commits when it can reach a D-Bus session bus, # so `sudo -u kiosk gsettings set ...` over SSH warns "Cannot autolaunch D-Bus # without X11" and silently leaves the default. It also cannot go in # /etc/dconf/db/local.d, because /usr/bin/gnome-kiosk sets # DCONF_PROFILE=gnomekiosk and that profile is `user-db:user` plus the shipped # compiled defaults — it never reads a system db. # # $XDG_RUNTIME_DIR is per-login, so this runs once per kiosk start and still # re-applies if someone changes the setting by hand. GSETTINGS_STAMP="${XDG_RUNTIME_DIR:-/tmp}/mirror-kiosk-gsettings.done" if [ ! -f "$GSETTINGS_STAMP" ]; then gsettings set org.gnome.desktop.session idle-delay 0 >/dev/null 2>&1 gsettings set org.gnome.desktop.screensaver lock-enabled false >/dev/null 2>&1 gsettings set org.gnome.desktop.interface enable-animations false >/dev/null 2>&1 log "applied idle/lock gsettings" : >"$GSETTINGS_STAMP" 2>/dev/null || : fi if pgrep -u "$(id -u)" -x firefox >/dev/null 2>&1; then # Up already. Nothing to do but check again in a moment. : elif command -v curl >/dev/null 2>&1 && ! curl -fsSL -m 10 -o /dev/null "$URL" 2>/dev/null; then # Don't park a "server not found" page on a wall display: keep polling and # load the page the moment the mirror server answers again. This is what # covers a reboot where the network has not come up yet. log "target not reachable yet: $URL" else mkdir -p "$PROFILE" 2>/dev/null log "launching: firefox --kiosk $URL" # --kiosk : fullscreen, no chrome, no exit affordances # --profile: our own dir, so the kiosk never collides with another profile # lock and never inherits anyone's history # `pgrep -x firefox` above can only be false here if a launch failed # outright, so a foreground run cannot wedge the loop. /usr/bin/firefox --kiosk --no-remote --profile "$PROFILE" "$URL" >>"$LOG" 2>&1 rc=$? log "firefox exited rc=$rc" fi sleep "$POLL" exec "$0" "$@"