#!/usr/sbin/openrc-run

set -e

description="Start vdevd early hotplug, or long-running if already promoted (coldplug promotes via vdevadm control --promote)"

depend()
{
	need mountkernfs.sh
	before checkfs.sh
	provide vdev udev
	keyword -containers
}


: "${VDEV_BIN:=/sbin/vdevd}"
[ -x "${VDEV_BIN}" ] || exit 0

: "${VDEV_MOUNTPOINT:=/dev}"
: "${VDEV_UDEV_CONTROL:=/run/udev/control}"

_json="${VDEV_GLOBALS_JSON:-/etc/vdev/vdevd.globals.json}"

if [ -r "${_json}" ] && command -v jq >/dev/null 2>&1; then
	VDEV_JSON_DATA="$(sed -E 's|//.*||g' "${_json}" |
		sed 's|/\*.*\*/||g' |
		grep -v '^$' |
		jq -r .)"
	export VDEV_JSON_DATA
else
	VDEV_CONFIG_DIR="/etc/vdev"
	VDEV_LONG_RUNNING="vdevd.conf"
	VDEV_EARLY="vdevd.early.conf"
	VDEV_STAMP_DIR="/run/vdev"
	VDEV_READY_STAMP="/run/vdev/vdev.ready"
fi

get_json_get_key() {
	printf '%s' "${VDEV_JSON_DATA}" | jq -r --arg k "$1" '
		.globals[$k] // empty'
}

get_json_get_config() {
	printf '%s' "${VDEV_JSON_DATA}" | jq -r --arg s "$1" '
		.profiles[$s].config // .states[$s].config // empty'
}

get_json_get_stamp() {
	printf '%s' "${VDEV_JSON_DATA}" | jq -r --arg s "$1" '
		.profiles[$s].stamp // .states[$s].stamp // empty'
}

if [ -n "${VDEV_JSON_DATA}" ]; then
	_config_dir="$(get_json_get_key "config-dir")"
	VDEV_CONFIG_DIR="${_config_dir:-/etc/vdev}"

	_long_running="$(get_json_get_config "long-running")"
	VDEV_LONG_RUNNING="${_long_running:-vdevd.conf}"

	_early="$(get_json_get_config "early")"
	VDEV_EARLY="${_early:-vdevd.early.conf}"

	_stamp_dir="$(get_json_get_key "states-stamp-dir")"
	VDEV_STAMP_DIR="${_stamp_dir:-/run/vdev}"

	_stamp_early="$(get_json_get_stamp "early")"
	VDEV_READY_STAMP="${VDEV_STAMP_DIR}/${_stamp_early:-vdev.ready}"
fi

VDEV_CONFIG="${VDEV_CONFIG_DIR}/${VDEV_LONG_RUNNING}"
VDEV_EARLY_CONFIG="${VDEV_CONFIG_DIR}/${VDEV_EARLY}"

source_ini_file() {
	local FILE_PATH="$1" NAMESPACE="$2" line KEY VALUE

	FILE_PATH=$1
	NAMESPACE=$2

	/bin/cat "${FILE_PATH}" | /bin/sed -e "s/.*\[.*\].*//g" -e '/^[[:space:]]*#/d' -e '/^[[:space:]]*$/d' |
	while read -r line; do
		KEY=$(echo "${line}" | /bin/sed "s/\(^.*\)=.*/\1/g")
		VALUE=$(echo "${line}" | /bin/sed "s/^.*=\(.*\)$/\1/g")

		if [ -n "${KEY}" ]; then
			echo "${NAMESPACE}${KEY}=${VALUE}"
		fi
	done
}

vdev_mkdir_for_file() {
	local _dir

	[ -n "${1:-}" ] || return 0
	_dir="$(echo "$1" | sed 's/[^/]\+$//g')"
	[ -n "${_dir}" ] && mkdir -p "${_dir}"
}

vdev_wait_for_stamp() {
	local _vs_path="$1" _vs_max="${2:-250}" _vs_sleep="${3:-0.2}" _vs_i=0

	while [ "${_vs_i}" -lt "${_vs_max}" ]; do
		[ -e "${_vs_path}" ] && return 0
		sleep "${_vs_sleep}"
		_vs_i=$((_vs_i + 1))
	done
	return 1
}

vdevd_start() {
	local _epid=""

	if [ -w /sys/kernel/uevent_helper ]; then
		echo > /sys/kernel/uevent_helper
	fi

	[ -x /sbin/restorecon ] && /sbin/restorecon -R "${VDEV_MOUNTPOINT}"

	mkdir -p "${VDEV_STAMP_DIR}"

	if [ -e "${VDEV_READY_STAMP}" ]; then
		eval "$(source_ini_file "${VDEV_CONFIG}" "vdev_config_")"
		vdev_mkdir_for_file "${vdev_config_logfile:-}"
		vdev_mkdir_for_file "${vdev_config_pidfile:-}"

		ebegin "Starting vdevd long-running (${VDEV_LONG_RUNNING})"
		"${VDEV_BIN}" -c "${VDEV_CONFIG}" "${VDEV_MOUNTPOINT}"
		eend $?

		if [ -n "${vdev_config_pidfile:-}" ]; then
			vdev_wait_for_stamp "${vdev_config_pidfile}" 50 0.1 || true
		fi
		return 0
	fi

	eval "$(source_ini_file "${VDEV_EARLY_CONFIG}" "vdev_early_")"
	vdev_mkdir_for_file "${vdev_early_logfile:-}"
	vdev_mkdir_for_file "${vdev_early_pidfile:-}"

	ebegin "Starting vdevd early hotplug (${VDEV_EARLY})"
	"${VDEV_BIN}" -c "${VDEV_EARLY_CONFIG}" "${VDEV_MOUNTPOINT}"
	eend $?

	if [ -n "${vdev_early_pidfile:-}" ]; then
		vdev_wait_for_stamp "${vdev_early_pidfile}" 50 0.1 || true
	fi
}

start_pre()
{
	if [ ! -d /sys/class/ ]; then
		eerror "vdev requires a mounted sysfs, not started"
		return 1
	fi

	if [ -d /sys/class/mem/null ] && [ ! -L /sys/class/mem/null ] ||
		[ -e /sys/block ] && [ ! -e /sys/class/block ]; then
		ewarn "CONFIG_SYSFS_DEPRECATED must not be selected"
		ewarn "Booting will continue in 30 seconds but many things will be broken"
		sleep 30
	fi

	eval "$(source_ini_file "${VDEV_CONFIG}" "vdev_config_")"
	if [ -z "${vdev_config_pidfile:-}" ]; then
		ewarn "No PID file defined in ${VDEV_CONFIG}. Please set the pidfile= directive."
		ewarn "You will be unable stop or restart vdevd with this script."
	fi

	eval "$(source_ini_file "${VDEV_EARLY_CONFIG}" "vdev_early_")"

	return 0
}

start()
{
	vdevd_start
}

stop()
{
	local _pid=""
	local _i=0

	ebegin "Stopping vdevd"
	_pid="$(vdevadm control --print-pid 2>/dev/null)" || true
	if [ -n "${_pid}" ]; then
		kill -TERM "${_pid}" 2>/dev/null || true
		while kill -0 "${_pid}" 2>/dev/null && [ "${_i}" -lt 100 ]; do
			sleep 0.1
			_i=$((_i + 1))
		done
	fi
	rm -f "${VDEV_READY_STAMP}"
	eend 0
}

status()
{
	local _pid=""

	_pid="$(vdevadm control --print-pid 2>/dev/null)" || true
	if [ -n "${_pid}" ]; then
		einfo "vdevd running (pid: ${_pid})"
		return 0
	fi
	eerror "vdevd is not running"
	return 3
}
