Harden HDMI recovery and add tested user installer release tooling
This commit is contained in:
1 parent
73e727ef4c
commit
d963224cb7
17 files changed
+903
-646
No files matched your search
@@ -1,77 +1,118 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u
|
||||
set -uo pipefail
|
||||
|
||||
LOGTAG="revive-monitor-fix"
|
||||
CONFIG_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/revive-monitor/config.env"
|
||||
|
||||
# Defaults. Override in ~/.config/revive-monitor/config.env
|
||||
MONITOR="HDMI-A-2"
|
||||
HDMI_SETTLE_SECONDS="5"
|
||||
VT_SWITCH_DELAY_SECONDS="1"
|
||||
VT_RETURN_SETTLE_SECONDS="2"
|
||||
|
||||
if [[ -r "$CONFIG_FILE" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$CONFIG_FILE"
|
||||
fi
|
||||
LOGTAG=revive-monitor-fix
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# Source-tree and installed layouts.
|
||||
COMMON="$SCRIPT_DIR/../lib/hyprland-hdmi-revive/common.sh"
|
||||
[[ -f "$COMMON" ]] || COMMON="$SCRIPT_DIR/../lib/common.sh"
|
||||
# shellcheck source=lib/common.sh
|
||||
source "$COMMON" || exit 1
|
||||
load_config || exit 1
|
||||
require_commands timeout loginctl hyprctl jq sudo flock stat sleep || exit 1
|
||||
prepare_runtime || exit 1
|
||||
|
||||
GRAPHICAL_VT="${1:-}"
|
||||
STATE_DIR="${XDG_RUNTIME_DIR:-/tmp}/revive-monitor"
|
||||
LOCK_FILE="$STATE_DIR/fix.lock"
|
||||
if [[ ! "$GRAPHICAL_VT" =~ ^[1-9][0-9]?$ ]] || (( GRAPHICAL_VT > 63 )); then
|
||||
log 'No valid graphical VT supplied (expected 1..63)'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$STATE_DIR"
|
||||
exec 9>"$STATE_DIR/fix.lock" || exit 1
|
||||
flock -n -E 75 9
|
||||
lock_status=$?
|
||||
if (( lock_status == 75 )); then
|
||||
log 'Recovery already running - skipping'
|
||||
exit 0
|
||||
elif (( lock_status != 0 )); then
|
||||
log 'Cannot acquire recovery lock'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log() {
|
||||
logger -t "$LOGTAG" -- "$*"
|
||||
SESSION="$(get_active_wayland_session)" || { log 'No active Wayland session - recovery skipped'; exit 0; }
|
||||
if [[ "${SESSION##*:}" != "$GRAPHICAL_VT" ]]; then
|
||||
log 'Requested VT does not belong to the active Wayland session'
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then
|
||||
log 'Missing HYPRLAND_INSTANCE_SIGNATURE; import the Hyprland environment into systemd --user'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
monitor_present() {
|
||||
timeout 5 hyprctl -j monitors |
|
||||
jq -e --arg name "$MONITOR" 'any(.[]; .name == $name)' >/dev/null 2>&1
|
||||
}
|
||||
|
||||
run_quiet() {
|
||||
"$@" >/dev/null 2>&1
|
||||
refresh_monitor() {
|
||||
timeout 5 hyprctl dispatch dpms on "$MONITOR" >/dev/null 2>&1 &&
|
||||
timeout 5 hyprctl dispatch focusmonitor "$MONITOR" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
if ! [[ "$GRAPHICAL_VT" =~ ^[1-9][0-9]*$ ]]; then
|
||||
log "No valid graphical VT supplied - recovery aborted"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$GRAPHICAL_VT" -eq 1 ]]; then
|
||||
TEMP_VT=2
|
||||
else
|
||||
TEMP_VT=1
|
||||
fi
|
||||
|
||||
exec 9>"$LOCK_FILE"
|
||||
if ! flock -n 9; then
|
||||
log "Recovery already running - skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Recovery started for $MONITOR on VT $GRAPHICAL_VT"
|
||||
sleep "$HDMI_SETTLE_SECONDS" || exit 1
|
||||
if ! monitor_present; then
|
||||
log "Hyprland IPC unavailable or configured output $MONITOR absent - recovery aborted"
|
||||
exit 1
|
||||
fi
|
||||
if ! refresh_monitor; then
|
||||
log 'Hyprland refresh failed - recovery aborted'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sleep "$HDMI_SETTLE_SECONDS"
|
||||
# Recheck after settling/IPC, immediately before the privileged switch.
|
||||
if [[ "$(get_active_wayland_session)" != "$SESSION" ]]; then
|
||||
log 'Active session changed while settling - recovery skipped'
|
||||
exit 0
|
||||
fi
|
||||
TEMP_VT=1
|
||||
[[ "$GRAPHICAL_VT" == 1 ]] && TEMP_VT=2
|
||||
# Check both directions before leaving the desktop (actual sudo can still fail).
|
||||
for vt in "$TEMP_VT" "$GRAPHICAL_VT"; do
|
||||
if ! timeout 5 sudo -n -l /usr/bin/chvt "$vt" >/dev/null 2>&1; then
|
||||
log "sudo permission check failed for VT $vt - recovery aborted"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
run_quiet hyprctl dispatch dpms on "$MONITOR"
|
||||
run_quiet hyprctl dispatch focusmonitor "$MONITOR"
|
||||
|
||||
sleep 1
|
||||
if [[ "$(get_active_wayland_session)" != "$SESSION" ]]; then
|
||||
log 'Active session changed during permission checks - recovery skipped'
|
||||
exit 0
|
||||
fi
|
||||
NEEDS_RETURN=0
|
||||
cleanup() {
|
||||
local status=$?
|
||||
trap - EXIT HUP INT TERM
|
||||
if (( NEEDS_RETURN )); then
|
||||
log "Attempting emergency return to graphical VT $GRAPHICAL_VT"
|
||||
if ! timeout 5 sudo -n /usr/bin/chvt "$GRAPHICAL_VT"; then
|
||||
log "Emergency return failed; manually switch to VT $GRAPHICAL_VT"
|
||||
status=1
|
||||
fi
|
||||
fi
|
||||
exit "$status"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
trap 'exit 129' HUP
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
|
||||
log "VT refresh: $TEMP_VT -> $GRAPHICAL_VT"
|
||||
|
||||
if ! sudo -n /usr/bin/chvt "$TEMP_VT"; then
|
||||
# Set before chvt to cover a signal arriving as the command completes.
|
||||
NEEDS_RETURN=1
|
||||
if ! timeout 5 sudo -n /usr/bin/chvt "$TEMP_VT"; then
|
||||
log "Failed to switch to temporary VT $TEMP_VT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sleep "$VT_SWITCH_DELAY_SECONDS"
|
||||
|
||||
if ! sudo -n /usr/bin/chvt "$GRAPHICAL_VT"; then
|
||||
sleep "$VT_SWITCH_DELAY_SECONDS" || exit 1
|
||||
if ! timeout 5 sudo -n /usr/bin/chvt "$GRAPHICAL_VT"; then
|
||||
log "Failed to return to graphical VT $GRAPHICAL_VT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sleep "$VT_RETURN_SETTLE_SECONDS"
|
||||
|
||||
run_quiet hyprctl dispatch dpms on "$MONITOR"
|
||||
run_quiet hyprctl dispatch focusmonitor "$MONITOR"
|
||||
|
||||
NEEDS_RETURN=0
|
||||
sleep "$VT_RETURN_SETTLE_SECONDS" || exit 1
|
||||
if ! refresh_monitor; then
|
||||
log 'Final Hyprland refresh failed'
|
||||
exit 1
|
||||
fi
|
||||
log "Recovery completed for $MONITOR"
|
||||
Reference in new issue
Block a user