#!/usr/sbin/openrc-run

set -e

description="Start vdevd, populate /dev and load drivers"

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


# Avoid any attempt to kill vdev by the shebang operator /lib/runit/invoke-run
# specified at the beginning of the runit run script
if [ -d /proc/1/. ] &&
   [ "$(cat /proc/1/comm)" = 'runit' ] &&
   [ "$(echo "$@" | cut -d' ' -f1,2)" = "stop --force-sysv" ]; then
	exit 0
fi

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

: "${VDEV_MOUNTPOINT:=/dev}"

_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_SYSFS="/sys"
	VDEV_CONFIG_DIR="/etc/vdev"
	VDEV_HELPERS_DIR="/lib/vdev"
	VDEV_EARLY="vdevd.1.conf"
	VDEV_LONG_RUNNING="vdevd.conf"
	VDEV_MODPROBE_STAMP="/run/vdev/vdevd-0-coldplug.ready"
	VDEV_EARLY_STAMP="/run/vdev/vdevd-1-coldplug.ready"
	VDEV_MODPROBE="vdevd.0.conf"
	VDEV_INITRD=""
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" '
		.states[$s].config // empty'
}

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

get_json_get_stamp_by_config() {
	printf '%s' "${VDEV_JSON_DATA}" | jq -r --arg c "$1" '
		.states[] | select(.config == $c) | .stamp // empty' | head -n 1
}

if [ -n "${VDEV_JSON_DATA}" ]; then
	_sysfs="$(get_json_get_key "sysfs")"
	VDEV_SYSFS="${_sysfs:-/sys}"

	_config_dir="$(get_json_get_key "config-dir")"
	VDEV_CONFIG_DIR="${_config_dir:-/etc/vdev}"

	_helpers_dir="$(get_json_get_key "helpers-dir")"
	VDEV_HELPERS_DIR="${_helpers_dir:-/lib/vdev}"

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

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

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

	_stamp_modprobe="$(get_json_get_stamp "modprobe")"
	VDEV_MODPROBE_STAMP="${VDEV_STAMP_DIR}/${_stamp_modprobe:-vdevd-0-coldplug.ready}"

	_stamp_early="$(get_json_get_stamp "early")"
	VDEV_EARLY_STAMP="${VDEV_STAMP_DIR}/${_stamp_early:-vdevd-1-coldplug.ready}"

	_modprobe_cfg="$(get_json_get_config "modprobe")"
	VDEV_MODPROBE="${_modprobe_cfg:-vdevd.0.conf}"

	_initrd_cfg="$(get_json_get_config "initrd")"
	VDEV_INITRD="${_initrd_cfg:-}"
fi

if [ -e "${VDEV_MODPROBE_STAMP}" ] && [ -e "${VDEV_EARLY_STAMP}" ]; then
	VDEV_CONFIG="${VDEV_CONFIG_DIR}/${VDEV_LONG_RUNNING}"
else
	VDEV_CONFIG="${VDEV_CONFIG_DIR}/${VDEV_EARLY}"
fi

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

	FILE_PATH=$1
	NAMESPACE=$2

	/bin/cat "${FILE_PATH}" | /bin/sed "s/.*\[.*\].*//g" |
	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_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
}

subshell() {
	vdev_wait_for_stamp "${VDEV_MODPROBE_STAMP}" || true
	vdev_wait_for_stamp "${VDEV_EARLY_STAMP}" || true

	if [ -d /proc/1/. ] && [ "$(cat /proc/1/comm)" != 'runit' ]; then
		unset VDEV_JSON_STATE 2>/dev/null || true
		exec "${VDEV_BIN}" -H -c "${VDEV_CONFIG_DIR}/${VDEV_LONG_RUNNING}" "${VDEV_MOUNTPOINT}"
	fi
}

subshell_1() {
	vdev_wait_for_stamp "${VDEV_MODPROBE_STAMP}" || true
	vdev_wait_for_stamp "${VDEV_EARLY_STAMP}" || true
	"${VDEV_BIN}" -1 -c "${VDEV_CONFIG_DIR}/vdevd.2.conf" "${VDEV_MOUNTPOINT}"
}

vdevd_start() {
	local VDEV_START_CONFIG vdev_log_dir vdev_pid_dir

	if [ -e "${VDEV_MODPROBE_STAMP}" ] && [ -e "${VDEV_EARLY_STAMP}" ]; then
		VDEV_START_CONFIG="${VDEV_CONFIG_DIR}/${VDEV_LONG_RUNNING}"
	else
		VDEV_START_CONFIG="${VDEV_CONFIG_DIR}/${VDEV_EARLY}"
	fi

	if [ "${VDEV_START_CONFIG}" != "${VDEV_CONFIG}" ]; then
		eval "$(source_ini_file "${VDEV_START_CONFIG}" "vdev_config_")"
	fi

	if [ "$(basename -- "${VDEV_START_CONFIG}")" = "${VDEV_EARLY}" ]; then
		rm -f "${VDEV_EARLY_STAMP}" 2>/dev/null || true
	fi

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

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

	if [ -n "${vdev_config_logfile:-}" ]; then
		vdev_log_dir="$(echo "${vdev_config_logfile}" | sed 's/[^/]\+$//g')"
		[ -n "${vdev_log_dir}" ] && mkdir -p "${vdev_log_dir}"
	fi

	if [ -n "${vdev_config_pidfile:-}" ]; then
		vdev_pid_dir="$(echo "${vdev_config_pidfile}" | sed 's/[^/]\+$//g')"
		[ -n "${vdev_pid_dir}" ] && mkdir -p "${vdev_pid_dir}"
	fi

	vdev_wait_for_stamp "${VDEV_MODPROBE_STAMP}" || true

	#export VDEV_JSON_STATE=modprobe
	#rm -f "${VDEV_MODPROBE_STAMP}"
	#/sbin/vdevd -c "${VDEV_CONFIG_DIR}/${VDEV_MODPROBE}" "${VDEV_MOUNTPOINT}"

	if [ "$(basename -- "${VDEV_START_CONFIG}")" = "${VDEV_LONG_RUNNING}" ]; then
		unset VDEV_JSON_STATE 2>/dev/null || true
	else
		export VDEV_JSON_STATE=early
	fi

	ebegin "Starting the system device event dispatcher"
	"${VDEV_BIN}" -c "${VDEV_START_CONFIG}" "$@" "${VDEV_MOUNTPOINT}"
	eend $?

	if [ "$(basename -- "${VDEV_START_CONFIG}")" = "${VDEV_EARLY}" ]; then
		( subshell ) &
		( subshell_1 ) &
	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

	return 0
}

start()
{
	vdevd_start
}

stop()
{
	ebegin "Stopping vdevd"
	if [ -x "${VDEV_BIN}" ]; then
		"${VDEV_BIN}" -k 2>/dev/null || true
	fi
	eend 0
}

status()
{
	local _vdev_peers _peer_list

	_vdev_peers="$("${VDEV_BIN}" -k --dry-run 2>/dev/null)"
	if [ -n "${_vdev_peers}" ]; then
		_peer_list="$(echo "${_vdev_peers}" | tr '\n' ' ' | sed 's/[[:space:]]*$//')"
		einfo "vdevd running (peer PIDs: ${_peer_list})"
		return 0
	fi
	eerror "vdevd is not running"
	return 3
}
