#!/bin/sh
# Copyright: 2026 Aitor <aitor@genuen.org>
# License: CC0-1.0
# s6-rc: seed early subsystems, promote listener; terminal coldplug (-1).
# Trail: /run/vdev/coldplug.log (headless-friendly).
# Worker may be backgrounded by OpenRC/s6/runit; vdevd -1 itself detaches
# (coldplug_only, no -f) — do not wrap it in another &.

_stamp_dir="${VDEV_STAMP_DIR:-/run/vdev}"
mkdir -p "${_stamp_dir}"
exec >>"${_stamp_dir}/coldplug.log" 2>&1
echo "vdev-coldplug: start $(date -u +%Y-%m-%dT%H:%M:%SZ) pid=$$"

if [ -r /lib/vdev/vdev-env.sh ]; then
	# shellcheck disable=SC1091
	. /lib/vdev/vdev-env.sh
elif [ -r /run/vdev/env ]; then
	# shellcheck disable=SC1091
	. /run/vdev/env
else
	echo "vdev-coldplug: no vdev-env.sh /run/vdev/env"
	exit 0
fi

_stamp_dir="${VDEV_STAMP_DIR:-/run/vdev}"
mkdir -p "${_stamp_dir}"

[ -x "${VDEV_BIN}" ] || exit 0
command -v vdevadm >/dev/null 2>&1 || exit 0

: "${VDEV_UDEV_CONTROL:=/run/udev/control}"
: "${VDEV_EARLY_SUBSYSTEMS:=graphics video4linux input hid i2c drm fd}"
: "${VDEV_PROMOTE_RETRIES:=5}"
: "${VDEV_EARLY_SETTLE_TIMEOUT:=15}"
: "${VDEV_LISTEN_GRACE:=1.0}"

VDEV_TERMINAL_CONFIG="${VDEV_CONFIG_DIR}/${VDEV_TERMINAL}"
VDEV_LONG_RUNNING_CONFIG="${VDEV_CONFIG_DIR}/${VDEV_LONG_RUNNING}"

# Prefer JSON array order (lightdm needs graphics/video4linux before input).
_gj="${VDEV_GLOBALS_JSON:-/etc/vdev/vdevd.globals.json}"
if [ -r "${_gj}" ] && command -v jq >/dev/null 2>&1; then
	_subs="$(sed -E 's|//.*||g' "${_gj}" | sed 's|/\*.*\*/||g' | grep -v '^$' |
		jq -r '(.profiles.early.subsystems // .states.early.subsystems // []) | .[]' 2>/dev/null |
		tr '\n' ' ')"
	[ -n "${_subs}" ] && VDEV_EARLY_SUBSYSTEMS="${_subs}"
elif [ -n "${VDEV_JSON_DATA:-}" ]; then
	_subs="$(printf '%s' "${VDEV_JSON_DATA}" | jq -r '
		(.profiles.early.subsystems // .states.early.subsystems // []) | .[]' 2>/dev/null |
		tr '\n' ' ')"
	[ -n "${_subs}" ] && VDEV_EARLY_SUBSYSTEMS="${_subs}"
fi

load_boot_vga_kms() {
	_boot_file=""
	_pci_dir=""
	_alias_string=""

	for _boot_file in /sys/bus/pci/devices/*/boot_vga; do
		[ -f "${_boot_file}" ] || continue
		[ "$(cat "${_boot_file}")" = "1" ] || continue
		_pci_dir=$(dirname "${_boot_file}")
		[ -f "${_pci_dir}/modalias" ] || continue
		_alias_string=$(cat "${_pci_dir}/modalias")
		case "${_alias_string}" in
			*v00008086*|*v000010DE*)
				modprobe -q "${_alias_string}" 2>/dev/null || true
				;;
			*v00001002*) ;;
			*)
				[ -n "${_alias_string}" ] && modprobe -q nouveau 2>/dev/null || true
				;;
		esac
		break
	done
}

vdev_promote_to_long_running() {
	_try=0
	_max="${VDEV_PROMOTE_RETRIES}"
	_cfg="${VDEV_LONG_RUNNING_CONFIG}"

	while [ "${_try}" -lt "${_max}" ]; do
		_try=$((_try + 1))
		if [ -n "${_cfg}" ]; then
			vdevadm control --promote="${_cfg}" && return 0
		else
			vdevadm control --promote && return 0
		fi
		sleep 0.1
	done
	return 1
}

# Do NOT use [ -e control ]: a dead AF_UNIX left by initrd vdevd survives on
# /dev/metadata/udev (via /run/udev symlink) and yields ECONNREFUSED.
# Wait until the live listener answers --print-pid.
vdev_wait_for_live_control() {
	_i=0
	_max=250
	_pid=""
	_err=""

	while [ "${_i}" -lt "${_max}" ]; do
		_err="$(vdevadm control --print-pid 2>&1)" && _pid="${_err}" || _pid=""
		if [ -n "${_pid}" ] && kill -0 "${_pid}" 2>/dev/null; then
			echo "vdev-coldplug: live control (pid=${_pid}); grace ${VDEV_LISTEN_GRACE}s"
			# Drop any race where we connected during rebind.
			sleep "${VDEV_LISTEN_GRACE}"
			_pid="$(vdevadm control --print-pid 2>/dev/null)" || _pid=""
			[ -n "${_pid}" ] && kill -0 "${_pid}" 2>/dev/null && return 0
		else
			# Stale socket: unlink so a late-starting vdevd can bind cleanly.
			if [ -e "${VDEV_UDEV_CONTROL}" ] && [ -z "${_pid}" ]; then
				case "${_err}" in
					*refused*|*Connection\ refused*|*No\ such\ file*)
						echo "vdev-coldplug: stale ${VDEV_UDEV_CONTROL} (${_err}); unlinking"
						rm -f "${VDEV_UDEV_CONTROL}" 2>/dev/null || true
						;;
				esac
			fi
		fi
		sleep 0.1
		_i=$((_i + 1))
	done
	return 1
}

load_boot_vga_kms

if ! vdev_wait_for_live_control; then
	echo "vdev-coldplug: no live /run/udev/control (vdevd-srv not listening?)"
	ls -la /run/udev /run/udev/control 2>&1 || true
	exit 0
fi

echo "vdev-coldplug: seeding: ${VDEV_EARLY_SUBSYSTEMS}"
for _s in ${VDEV_EARLY_SUBSYSTEMS}; do
	if vdevadm trigger -s "${_s}" -a add; then
		echo "vdev-coldplug: trigger ${_s} ok"
	else
		echo "vdev-coldplug: trigger ${_s} rc=$?"
	fi
done

vdevadm settle -t "${VDEV_EARLY_SETTLE_TIMEOUT}" || \
	echo "vdev-coldplug: settle rc=$?"

if vdev_promote_to_long_running; then
	printf '%s\n' "${VDEV_LONG_RUNNING_CONFIG}" >"${_stamp_dir}/promoted.conf"
	echo "vdev-coldplug: promoted to ${VDEV_LONG_RUNNING_CONFIG}"
else
	echo "vdev-coldplug: promote failed"
	rm -f "${_stamp_dir}/promoted.conf"
	vdevadm control --print-pid 2>&1 || true
fi

# coldplug_only (-1, no -f) already detaches; do not background again.
"${VDEV_BIN}" -1 -c "${VDEV_TERMINAL_CONFIG}" "${VDEV_MOUNTPOINT}"

exit 0
