#!/bin/sh # shikane-watchdog: auto-recover shikane from its deadlock bug. # # Bug (shikane 1.1.1): if a variant application is discarded mid-way # (e.g. by a failing DP atomic commit), the daemon gets stuck in # VariantInProgress/Discarded. Two flavors observed: # loud: keeps logging # [WARN shikane::variant] Received invalid input AtomicChangeDone at state Discarded # silent: no WARN at all; the variant state ends at "Discarded" and the # daemon state machine simply never leaves VariantInProgress # `shikanectl reload` does not recover it; a service restart does # (re-applies the active profile in ~1s). # # A healthy daemon's last state-machine line is "VariantApplied". set -u unit=shikane stuck=0 logs3=$(journalctl --user -u "$unit" -q --since "-3min" 2>/dev/null) [ -z "$logs3" ] && exit 0 latest3=$(printf '%s\n' "$logs3" | grep 'daemon::state_machine' | tail -1) if [ -n "$latest3" ]; then case "$latest3" in *VariantApplied*) : ;; # healthy *) # Daemon still InProgress. Deadlock signatures (a legitimate # slow transition has neither): # loud: the WARN from the discarded-variant race # silent: variant discarded but daemon never processed it printf '%s\n' "$logs3" | grep -q 'invalid input' && stuck=1 if [ "$stuck" -eq 0 ]; then lastvariant=$(printf '%s\n' "$logs3" \ | grep 'shikane::variant' | grep 'New variant state' | tail -1) case "$lastvariant" in *Discarded*) stuck=1 ;; esac fi ;; esac else # No state change in the last 3 min — check the last known state # (unbounded: a stuck daemon may have gone silent long ago, # e.g. across a laptop suspend) lastline=$(journalctl --user -u "$unit" -q -n 500 2>/dev/null \ | grep 'daemon::state_machine' | tail -1) case "$lastline" in '' | *VariantApplied*) : ;; # stable and applied, or nothing to check *) stuck=1 ;; # stuck in-progress/discarded, any age esac fi if [ "$stuck" -eq 1 ]; then echo "shikane-watchdog: shikane stuck, restarting" >&2 systemctl --user restart "$unit" fi