#!/bin/sh -e
### BEGIN INIT INFO
# Provides:          vdev udev
# Required-Start:    mountkernfs
# Default-Start:     S
# Default-Stop:      0 6
# Short-Description: Start vdevd early hotplug, or long-running if vdev.ready exists.
# Description:	     vdev-coldplug seeds via vdevadm and promotes with
#                    `vdevadm control --promote`; vdev does not wait for it.
### END INIT INFO

PATH="/sbin:/bin:/usr/bin"

SCRIPTNAME=$0

if [ -z "$VDEV_BIN" ]; then 
    VDEV_BIN=/sbin/vdevd
fi

[ -x $VDEV_BIN ] || exit 0

if [ -z "$VDEV_MOUNTPOINT" ]; then
    VDEV_MOUNTPOINT=/dev
fi

: "${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"

. /lib/lsb/init-functions

source_ini_file() {
    local FILE_PATH NAMESPACE 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 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
}

if [ ! -d /sys/class/ ]; then
    log_failure_msg "vdev requires a mounted sysfs, not started"
    log_end_msg 1
fi

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

eval $(source_ini_file $VDEV_CONFIG "vdev_config_")
eval $(source_ini_file $VDEV_EARLY_CONFIG "vdev_early_")

if [ -z "$vdev_config_pidfile" ]; then 
    log_warning_msg "No PID file defined in $VDEV_CONFIG.  Please set the pidfile= directive."
    log_warning_msg "You will be unable stop or restart vdevd with this script."
fi

vdev_wait_for_stamp() {
    _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
}

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

vdevd_start() {
    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:-/run/vdev}"

    if [ -e "$VDEV_READY_STAMP" ]; then
        vdev_mkdir_for_file "$vdev_config_logfile"
        vdev_mkdir_for_file "$vdev_config_pidfile"

        log_daemon_msg "Starting vdevd long-running" "vdevd"
        "$VDEV_BIN" -c "$VDEV_CONFIG" "$VDEV_MOUNTPOINT"
        log_end_msg $?

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

    vdev_mkdir_for_file "$vdev_early_logfile"
    vdev_mkdir_for_file "$vdev_early_pidfile"

    log_daemon_msg "Starting vdevd early hotplug" "vdevd.early"
    "$VDEV_BIN" -c "$VDEV_EARLY_CONFIG" "$VDEV_MOUNTPOINT"
    log_end_msg $?

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

vdevd_stop() {
    _pid=""

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

vdev_eventfs_umount_on_shutdown() {
    _rl="$(/sbin/runlevel 2>/dev/null | awk '{print $2}')"
    if [ "$_rl" = "0" ] || [ "$_rl" = "6" ]; then
        if [ -x "/lib/vdev/vdev-eventfs-umount.sh" ]; then
            "/lib/vdev/vdev-eventfs-umount.sh" "$VDEV_MOUNTPOINT"
        fi
    fi
}

case "$1" in
   start)
      vdevd_start
      ;;
   stop)
      vdevd_stop
      vdev_eventfs_umount_on_shutdown
      ;;
   restart)
      vdevd_stop
      sleep 1
      vdevd_start
      ;;
   status)
      _pid=""
      _pid="$(vdevadm control --print-pid 2>/dev/null)" || true
      if [ -n "$_pid" ]; then
         echo "vdevd running (pid: $_pid)"
         exit 0
      fi
      echo "vdevd is not running."
      exit 3
      ;;
   *)
      echo "Usage: $SCRIPTNAME {start|stop|restart|status}" >&2
      exit 1
      ;;
esac
