diff --git a/scripts/kiosk/90-carwatch-kiosk.rules b/scripts/kiosk/90-carwatch-kiosk.rules index 0ebf746..f3d39c9 100644 --- a/scripts/kiosk/90-carwatch-kiosk.rules +++ b/scripts/kiosk/90-carwatch-kiosk.rules @@ -1,3 +1,14 @@ -# Start the CarWatch kiosk when a display is plugged in (DRM hotplug event). +# Start the CarWatch kiosk when a panel is plugged in. # Installed by install-kiosk*.sh with the kiosk user substituted for KUSER. -SUBSYSTEM=="drm", ACTION=="change", ENV{HOTPLUG}=="1", TAG+="systemd", ENV{SYSTEMD_WANTS}+="carwatch-kiosk@KUSER.service" +# +# RUN, not SYSTEMD_WANTS: Wants= on the device unit fires once when card0 +# becomes active, and card0 stays active across unplug/replug (codexmb, #60). +# +# ENV{HOTPLUG}=="1" is load-bearing: the kernel sets it only on real connector +# hotplug (drivers/gpu/drm/drm_sysfs.c, drm_sysfs_hotplug_event). The kiosk's +# own start switches the VT, which also emits a DRM "change" event WITHOUT +# HOTPLUG; a rule keyed on any change event therefore restarts the kiosk from +# its own start: 517 starts in a minute, measured on the VTA, 15 Sep 2026. +# The price is that `udevadm trigger` cannot exercise this rule (synthetic +# events carry no HOTPLUG); only a real plug does. +SUBSYSTEM=="drm", KERNEL=="card[0-9]*", ACTION=="change", ENV{HOTPLUG}=="1", RUN+="/usr/local/bin/carwatch-kiosk-hotplug.sh KUSER" diff --git a/scripts/kiosk/carwatch-kiosk-hotplug.sh b/scripts/kiosk/carwatch-kiosk-hotplug.sh new file mode 100755 index 0000000..32fabac --- /dev/null +++ b/scripts/kiosk/carwatch-kiosk-hotplug.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Called by udev (90-carwatch-kiosk.rules) on every DRM hotplug event, with the +# kiosk user as $1. Asks systemd to start the kiosk unit and returns at once; +# the launcher itself decides whether a panel is connected (exit 78 if not). +# A plain RUN is used instead of SYSTEMD_WANTS because Wants on a device unit +# fires only when the device becomes active, and card0 stays active across +# panel unplug/replug (codexmb, CarWatch #60 review). +UNIT="carwatch-kiosk@${1:?kiosk user}.service" +# A unit left 'failed' (start-limit-hit, a crashed browser) would refuse the +# start; clear that first, then ask for the start without waiting. +/usr/bin/systemctl reset-failed "$UNIT" 2>/dev/null || true +exec /usr/bin/systemctl start --no-block "$UNIT" diff --git a/scripts/kiosk/carwatch-kiosk.service b/scripts/kiosk/carwatch-kiosk.service index 022687d..56452e5 100644 --- a/scripts/kiosk/carwatch-kiosk.service +++ b/scripts/kiosk/carwatch-kiosk.service @@ -1,5 +1,11 @@ [Unit] Description=CarWatch dash kiosk on the local HDMI panel (cage + Chromium) +# A panel plug can arrive as a burst of DRM change events, each a start +# attempt that exits 78 within a second when the panel is not (yet) there; +# the default 5-in-10-s limit turned such a burst into 'start-limit-hit' +# and a unit stuck 'failed' (measured on the VTA, 15 Sep 2026). +StartLimitIntervalSec=30 +StartLimitBurst=20 After=carwatch-chat.service systemd-user-sessions.service Wants=carwatch-chat.service Conflicts=getty@tty1.service diff --git a/scripts/kiosk/carwatch-kiosk.sh b/scripts/kiosk/carwatch-kiosk.sh index 54b5176..1433c46 100755 --- a/scripts/kiosk/carwatch-kiosk.sh +++ b/scripts/kiosk/carwatch-kiosk.sh @@ -42,8 +42,10 @@ panel_connected() { done return 1 } +# stderr under a TTY-bound unit does not reach the journal; say it there too. +say() { echo "carwatch-kiosk: $*" >&2; command -v logger >/dev/null 2>&1 && logger -t carwatch-kiosk -- "$*"; } if ! panel_connected; then - echo "carwatch-kiosk: no display connected, not starting the browser" >&2 + say "no display connected, not starting the browser" exit "$NO_PANEL_EXIT" fi TOKEN_FILE="${CARWATCH_DASH_TOKEN_FILE:-$HOME/.carwatch/dash-token}" @@ -88,7 +90,7 @@ trap 'kill "$BROWSER" 2>/dev/null' TERM INT # Watch the panel while the browser runs: unplugged panel = stop drawing. while kill -0 "$BROWSER" 2>/dev/null; do if ! panel_connected; then - echo "carwatch-kiosk: display disconnected, stopping the browser" >&2 + say "display disconnected, stopping the browser" kill "$BROWSER" 2>/dev/null wait "$BROWSER" 2>/dev/null exit "$NO_PANEL_EXIT" diff --git a/scripts/kiosk/install-kiosk-ubuntu.sh b/scripts/kiosk/install-kiosk-ubuntu.sh index eb0fa53..5b572bc 100755 --- a/scripts/kiosk/install-kiosk-ubuntu.sh +++ b/scripts/kiosk/install-kiosk-ubuntu.sh @@ -17,6 +17,7 @@ cat > /etc/systemd/system/carwatch-kiosk@.service.d/snap-chromium.conf <<'CONF' Environment=CARWATCH_CHROMIUM=/snap/bin/chromium CONF install -m 644 "$HERE/carwatch-kiosk.service" /etc/systemd/system/carwatch-kiosk@.service +install -m 755 "$HERE/carwatch-kiosk-hotplug.sh" /usr/local/bin/carwatch-kiosk-hotplug.sh sed "s/KUSER/${KUSER}/g" "$HERE/90-carwatch-kiosk.rules" > /etc/udev/rules.d/90-carwatch-kiosk.rules udevadm control --reload-rules 2>/dev/null || true usermod -aG video,input,render "$KUSER" 2>/dev/null || true diff --git a/scripts/kiosk/install-kiosk.sh b/scripts/kiosk/install-kiosk.sh index 2bcf44e..c7fc19b 100755 --- a/scripts/kiosk/install-kiosk.sh +++ b/scripts/kiosk/install-kiosk.sh @@ -9,6 +9,7 @@ apt-get update -q apt-get install -y -q --no-install-recommends cage chromium seatd grim fonts-noto-color-emoji install -m 755 "$HERE/carwatch-kiosk.sh" /usr/local/bin/carwatch-kiosk.sh install -m 644 "$HERE/carwatch-kiosk.service" /etc/systemd/system/carwatch-kiosk@.service +install -m 755 "$HERE/carwatch-kiosk-hotplug.sh" /usr/local/bin/carwatch-kiosk-hotplug.sh sed "s/KUSER/${KUSER}/g" "$HERE/90-carwatch-kiosk.rules" > /etc/udev/rules.d/90-carwatch-kiosk.rules udevadm control --reload-rules 2>/dev/null || true usermod -aG video,input,render "$KUSER" 2>/dev/null || true