#!/bin/sh
# Не выпускать браузер, пока показывать некуда и нечего.
#
# Два ожидания, и оба нужны:
#
#  1. Подключённый экран. cog --platform=drm требует CRTC; если телевизор ещё
#     не воткнут или не ответил по HDMI, ядро пишет «Cannot find any crtc or
#     sizes», и cog падает. Плату включают раньше телевизора чаще, чем кажется.
#
#  2. Агент. Иначе первое, что нарисует браузер, — ошибка соединения, а не
#     страница, и человек перед телевизором ничего с этим сделать не может.
#
# Выходим с ошибкой, а не ждём вечно: юнит перезапустится и попробует снова.
# Лимит перезапусков у него снят, так что попытки не кончатся.
set -eu

PORT="${SCREEN_PORT:-3000}"
TIMEOUT="${DASHDASH_WAIT_TIMEOUT:-120}"
BOOT_PAGE="${DASHDASH_BOOT_PAGE:-/usr/local/share/dashdash/boot.html}"
BOOT_PID=""

display_connected() {
	for status in /sys/class/drm/card*-*/status; do
		[ -f "$status" ] || continue
		[ "$(cat "$status" 2>/dev/null)" = "connected" ] && return 0
	done
	return 1
}

i=0
while [ "$i" -lt "$TIMEOUT" ]; do
	if display_connected; then
		break
	fi
	i=$((i + 1))
	sleep 1
done
if ! display_connected; then
	echo "dashdash: за ${TIMEOUT}с не появилось ни одного подключённого экрана" >&2
	exit 1
fi

# Экран есть, агента ещё нет — и это та самая пара минут, в которые человек
# перед телевизором видит чёрный прямоугольник и решает, что устройство
# сломано. Показываем заставку тем же cog, только со страницы с диска: сети
# на этом этапе может не быть вовсе.
show_boot_screen() {
	[ -f "$BOOT_PAGE" ] || return 0
	command -v cog >/dev/null 2>&1 || return 0
	# shellcheck disable=SC2086 # COG_PARAMS — это набор аргументов, а не строка
	cog --platform=drm ${COG_PARAMS-} "file://${BOOT_PAGE}" >/dev/null 2>&1 &
	BOOT_PID=$!
}

# Жив ли процесс заставки.
boot_screen_alive() {
	[ -n "$BOOT_PID" ] || return 1
	kill -0 "$BOOT_PID" 2>/dev/null
}

# Заставку надо не просто убить, а дождаться её смерти: пока процесс жив, он
# держит DRM master, и настоящий cog стартует в «Permission denied».
hide_boot_screen() {
	[ -n "$BOOT_PID" ] || return 0
	kill "$BOOT_PID" 2>/dev/null || true
	wait "$BOOT_PID" 2>/dev/null || true
	BOOT_PID=""
	# Ядру нужен момент, чтобы отпустить мастера после закрытия устройства.
	sleep 1
}

trap hide_boot_screen EXIT INT TERM

show_boot_screen

i=0
while [ "$i" -lt "$TIMEOUT" ]; do
	if curl -fsS -o /dev/null "http://127.0.0.1:${PORT}/health"; then
		hide_boot_screen
		exit 0
	fi

	# При холодном старте cog часто не переживает первую попытку: ядро только
	# что подняло DRM, разъём ещё «дребезжит», и процесс умирает молча. Один
	# запуск на этом и заканчивался — заставка появлялась только при
	# перезапуске сервисов вручную, когда всё уже прогрето. Поэтому пробуем
	# снова, не чаще раза в пять секунд, чтобы не устроить круг перезапусков.
	if ! boot_screen_alive && [ $((i % 5)) -eq 0 ]; then
		BOOT_PID=""
		show_boot_screen
	fi

	i=$((i + 1))
	sleep 1
done

echo "dashdash: агент не ответил на :${PORT} за ${TIMEOUT}с" >&2
exit 1
