readonly PC_NAME="ProcessController"
readonly PC_PATH="/opt/MXB/bin/${PC_NAME}"

# Checks if the script is running inside the Linux rescue environment
is_rescue_environment()
{
    # Perform the check only once because /proc/cmdline doesn't change after starting a computer
    [ -z "${IS_RESCUE_ENVIRONMENT}" ] || return $IS_RESCUE_ENVIRONMENT

    local cmdline
    read -r cmdline </proc/cmdline

    for option in $cmdline; do
        case $option in
            cove_rescue_media)
                readonly IS_RESCUE_ENVIRONMENT=0
                return $IS_RESCUE_ENVIRONMENT
                ;;
        esac
    done

    readonly IS_RESCUE_ENVIRONMENT=1
    return $IS_RESCUE_ENVIRONMENT
}

# Returns ProcessController's process id
get_pc_pid()
{
    ps aux | awk -v pc_name=${PC_NAME} '$0 ~ pc_name && !/awk/ {print $2}'
}

# systemd is not fully available to manage services when the Relax-and-Recover
# rescue media is running in automatic mode because it blocks the boot-up process.
# Mocks ProcessController service in the Linux rescue environment.
service_in_rescue_environment()
{
    local _action="$1"

    case "$_action" in
        install|uninstall|zap)
            # ProcessController service unit should not be installed/uninstalled
            # since the service is not used in the rescue environment at all.
            ;;
        exists)
            # Test if ProcessController is running at the moment
            [ -n "$(get_pc_pid)" ]
            ;;
        stop)
            local pid="$(get_pc_pid)"
            [ -z "$pid" ] || { /bin/kill -TERM "${pid}" && \
            while [ -n "$pid" ]; do \
                sleep 1; \
                pid="$(get_pc_pid)"; \
            done }
            ;;
        start)
            local pid="$(get_pc_pid)"
            # Start ProcessController only if it is not running
            [ -n "$pid" ] || ${PC_PATH} serve
            ;;
        restart)
            local pid="$(get_pc_pid)"
            # Start ProcessController if it is not running
            [ -n "$pid" ] && /bin/kill -HUP "${pid}" || ${PC_PATH} serve
            ;;
    esac
}

service_ctl_simple()
{
    local _action="$1"
    local _initScript="$2"

    case "$_action" in
        exists)
            test -x "$_initScript"
            ;;
        stop)
            "$_initScript" stop
            ;;
        start)
            "$_initScript" start
            ;;
        restart)
            "$_initScript" restart
            ;;
        zap)
            "$_initScript" zap
            ;;
    esac

    return $?
}

# https://www.freedesktop.org/software/systemd/man/latest/sd_booted.html
sd_booted()
{
    test -d /run/systemd/system/
}

have_sysv_compat()
{
    command -v systemctl >/dev/null 2>&1 && systemctl --version | grep -q -e "+SYSVINIT"
}

error()
{
    echo "error:" "$@"
    echo ""
    exit 1
}

service_ctl_linux_systemv()
{
    local _action="$1"
    local _initName="$2"
    local _initScript="/etc/init.d/$_initName"

    case "$_action" in
        install)
            local _initScriptSrc="$3"
            [ ! -e "$_initScriptSrc" ] && error "cannot find $_initScriptSrc in the package"
            cp -Hp "$_initScriptSrc" "$_initScript" 2>/dev/null || return 1
            chmod 0755 "$_initScript"

            if [ -x /usr/sbin/update-rc.d ]; then
                /usr/sbin/update-rc.d "$_initName" defaults
            elif [ -x /sbin/rc-update ]; then
                /sbin/rc-update add "$_initName" default
            elif [ -x /sbin/chkconfig ]; then
                /sbin/chkconfig --add "$_initName"
            elif [ -x /usr/lib/lsb/install_initd ]; then
                /usr/lib/lsb/install_initd "$_initScript"
            else
                RC_DIR="/etc/rc.d"
                [ ! -d "$RC_DIR" ] && RC_DIR="/etc"
                [ ! -d "$RC_DIR/rc3.d" ] && return 1
                for i in 3 4 5; do
                    ln -sf "$_initScript" "$RC_DIR/rc${i}.d/S90$_initName"
                done
                for i in 1 6; do
                    ln -sf "$_initScript" "$RC_DIR/rc${i}.d/K10$_initName"
                done
            fi
            ;;
        uninstall)
            if [ -x /usr/sbin/update-rc.d ]; then
                /usr/sbin/update-rc.d -f "$_initName" remove
            elif [ -x /sbin/rc-update ]; then
                /sbin/rc-update del "$_initName"
            elif [ -x /sbin/chkconfig ]; then
                /sbin/chkconfig --del "$_initName"
            elif [ -x /usr/lib/lsb/remove_initd ]; then
                /usr/lib/lsb/remove_initd "$_initScript"
            else
                RC_DIR="/etc/rc.d"
                [ ! -d "$RC_DIR" ] && RC_DIR="/etc"
                [ ! -d "$RC_DIR/rc3.d" ] && return 1
                rm -f "$RC_DIR/rc"?".d/"???"$_initName"
            fi

            rm -f "$_initScript"
            ;;
        *)
            service_ctl_simple "$_action" "$_initScript"
            ;;
    esac

    return $?
}

service_ctl_linux_systemd()
{
    local _action="$1"
    local _initName="$2"
    local _initScript="/etc/init.d/$_initName"
    local _systemdService="/etc/systemd/system/$_initName.service"

    case "$_action" in
        install)
            local _initScriptSrc="$3"
            local _systemdServiceSrc="$4"

            [ ! -e "$_systemdServiceSrc" ] && error "cannot find $_systemdServiceSrc in the package"
            cp -Hp "$_systemdServiceSrc" "$_systemdService"
            chmod 0644 "$_systemdService"

            if have_sysv_compat; then
                service_ctl_linux_systemv install $_initName $_initScriptSrc || true
            fi

            systemctl daemon-reload
            systemctl enable --quiet $_initName
            ;;
        uninstall)
            systemctl disable --quiet "$_initName" || true

            if have_sysv_compat && service_ctl_linux_systemv exists $_initName; then
                service_ctl_linux_systemv uninstall $_initName || true
            fi

            rm -f "$_systemdService" || true

            systemctl daemon-reload
            ;;
        exists)
            local service="$_initName.service"
            test "$(systemctl list-unit-files --no-legend "$service" | awk '{ print $1 }')" = "$service" -o \
                 "$(systemctl list-units --no-legend --all "$service" | awk '{ print $1 }')" = "$service"
            ;;
        stop)
            systemctl stop "$_initName"
            ;;
        start)
            systemctl start "$_initName"
            ;;
        restart)
            systemctl restart "$_initName"
            ;;
        zap)
            ;;
    esac

    return $?
}

get_service_ctl()
{
    local system
    system="$(uname -s)"

    case "$system" in
        Linux)
            if is_rescue_environment; then
                echo "service_in_rescue_environment"
            else
                if sd_booted; then
                    echo "service_ctl_linux_systemd"
                else
                    echo "service_ctl_linux_systemv"
                fi
            fi
            ;;
        *)
            error "platform not supported"
            ;;
    esac
}
