#!/bin/sh
# Keep the xcatd SysV init script only on systems that actually use a
# legacy init implementation.  XCAT_COMPAT_ROOT is used by image builders
# and tests to operate on a target root without chrooting into it.

compat_root=${XCAT_COMPAT_ROOT:-}
compat_root=${compat_root%/}
xcat_root=${XCATROOT:-/opt/xcat}

case "$xcat_root" in
    /*) ;;
    *)
        echo "XCATROOT must be an absolute path" >&2
        exit 2
        ;;
esac

legacy_init="$compat_root/etc/init.d/xcatd"
legacy_template="$compat_root$xcat_root/share/xcat/scripts/xcatd"
managed_marker="$compat_root/var/lib/xcat/xcatd-init-compat-managed"

systemd_os_release_status()
{
    os_release="$compat_root/etc/os-release"
    [ -r "$os_release" ] || return 2

    os_id=
    os_version=
    while IFS='=' read -r key value; do
        value=${value#\"}
        value=${value%\"}
        value=${value#\'}
        value=${value%\'}
        case "$key" in
            ID) os_id=$value ;;
            VERSION_ID) os_version=$value ;;
        esac
    done < "$os_release"

    # Debian testing/sid intentionally omits VERSION_ID.
    if [ "$os_id" = "debian" ] && [ -z "$os_version" ]; then
        return 0
    fi

    os_major=${os_version%%.*}
    case "$os_major" in
        ''|*[!0-9]*) return 2 ;;
    esac

    case "$os_id" in
        rhel|centos|rocky|almalinux|ol|scientific)
            [ "$os_major" -ge 7 ] && return 0
            return 1
            ;;
        fedora|ubuntu)
            [ "$os_major" -ge 15 ] && return 0
            return 1
            ;;
        debian)
            [ "$os_major" -ge 8 ] && return 0
            return 1
            ;;
        sles|sled|opensuse|opensuse-leap|opensuse-tumbleweed)
            [ "$os_major" -ge 12 ] && return 0
            return 1
            ;;
        *)
            return 2
            ;;
    esac

}

uses_systemd()
{
    detection_mode=${1:-compat}

    if [ -d "$compat_root/run/systemd/system" ]; then
        return 0
    fi

    if [ "$detection_mode" = explicit ]; then
        if [ -L "$compat_root/sbin/init" ]; then
            init_target=$(readlink "$compat_root/sbin/init" 2>/dev/null || true)
            case "$init_target" in
                *systemd*) return 0 ;;
                *) return 1 ;;
            esac
        fi

        if [ -e "$compat_root/sbin/init" ]; then
            return 1
        fi

        if systemd_os_release_status; then
            return 0
        else
            os_release_status=$?
            if [ "$os_release_status" -eq 1 ]; then
                return 1
            fi
        fi
    fi

    # Preserve the historical fallback order unless every package lifecycle
    # phase has explicitly opted in to authoritative /sbin/init detection.
    if [ -x "$compat_root/usr/lib/systemd/systemd" ] ||
        [ -x "$compat_root/lib/systemd/systemd" ]; then
        return 0
    fi

    if systemd_os_release_status; then
        return 0
    fi

    if [ -L "$compat_root/sbin/init" ]; then
        init_target=$(readlink "$compat_root/sbin/init" 2>/dev/null || true)
        case "$init_target" in
            *systemd*) return 0 ;;
        esac
    fi

    return 1
}

can_use_systemctl()
{
    [ -d /run/systemd/system ] && command -v systemctl >/dev/null 2>&1
}

legacy_state()
{
    detection_mode=${1:-compat}

    if [ "$detection_mode" = debian-layout ]; then
        set -- "$compat_root"/etc/rc?.d/S??xcatd
    else
        set -- \
            "$compat_root"/etc/rc.d/rc?.d/S??xcatd \
            "$compat_root"/etc/rc?.d/S??xcatd \
            "$compat_root"/etc/init.d/rc?.d/S??xcatd
    fi
    for legacy_link in "$@"; do
        if [ -e "$legacy_link" ] || [ -L "$legacy_link" ]; then
            echo enabled
            return
        fi
    done

    if [ "$detection_mode" = debian-layout ]; then
        set -- "$compat_root"/etc/rc?.d/K??xcatd
    else
        set -- \
            "$compat_root"/etc/rc.d/rc?.d/K??xcatd \
            "$compat_root"/etc/rc?.d/K??xcatd \
            "$compat_root"/etc/init.d/rc?.d/K??xcatd
    fi
    for legacy_link in "$@"; do
        if [ -e "$legacy_link" ] || [ -L "$legacy_link" ]; then
            echo disabled
            return
        fi
    done

    echo unregistered
}

has_legacy_provenance()
{
    [ -e "$legacy_init" ] || [ -L "$legacy_init" ] ||
        [ -f "$managed_marker" ]
}

legacy_transition_state()
{
    transition_state=$(legacy_state)
    if [ "$transition_state" != unregistered ]; then
        echo "$transition_state"
        return
    fi

    transition_state=$(systemd_state)
    case "$transition_state" in
        enabled|masked)
            echo "$transition_state"
            ;;
        disabled)
            if has_legacy_provenance; then
                echo unregistered
            else
                echo disabled
            fi
            ;;
    esac
}

systemd_is_masked()
{
    for systemd_mask in \
        "$compat_root/etc/systemd/system/xcatd.service" \
        "$compat_root/run/systemd/system/xcatd.service"
    do
        if [ -L "$systemd_mask" ]; then
            systemd_mask_target=$(readlink "$systemd_mask" 2>/dev/null || true)
            case "$systemd_mask_target" in
                /dev/null|*/dev/null)
                    return 0
                    ;;
            esac
        fi
    done

    return 1
}

systemd_links_enabled()
{
    for systemd_link in \
        "$compat_root"/etc/systemd/system/*.wants/xcatd.service \
        "$compat_root"/etc/systemd/system/*.requires/xcatd.service \
        "$compat_root"/run/systemd/system/*.wants/xcatd.service \
        "$compat_root"/run/systemd/system/*.requires/xcatd.service
    do
        if [ -e "$systemd_link" ] || [ -L "$systemd_link" ]; then
            return 0
        fi
    done

    return 1
}

systemctl_enable_state()
{
    detection_mode=${1:-compat}

    command -v systemctl >/dev/null 2>&1 || return 1

    if [ -n "$compat_root" ]; then
        detected_systemd_state=$(systemctl --root="$compat_root" is-enabled xcatd.service 2>/dev/null || true)
    else
        detected_systemd_state=$(systemctl is-enabled xcatd.service 2>/dev/null || true)
    fi
    if [ "$detection_mode" = precise ]; then
        case "$detected_systemd_state" in
            enabled|enabled-runtime) echo enabled ;;
            masked|masked-runtime) echo masked ;;
            disabled|linked|linked-runtime|alias|static|indirect|generated|transient)
                echo disabled
                ;;
            *) return 1 ;;
        esac
    else
        case "$detected_systemd_state" in
            enabled*) echo enabled ;;
            masked*) echo masked ;;
            disabled|linked|linked-runtime|alias|static|indirect|generated|transient)
                echo disabled
                ;;
            *) return 1 ;;
        esac
    fi
}

systemd_state()
{
    detection_mode=${1:-compat}
    detected_systemd_state=

    if systemd_is_masked; then
        echo masked
        return
    fi

    if [ "$detection_mode" = allow-unknown ]; then
        detected_systemd_state=$(systemctl_enable_state precise || true)
        if [ "$detected_systemd_state" = masked ]; then
            echo masked
            return
        fi
    fi

    # Preserve the historical host-first order for existing callers.
    if [ "$detection_mode" = compat ] && [ -z "$compat_root" ]; then
        detected_systemd_state=$(systemctl_enable_state || true)
        case "$detected_systemd_state" in
            enabled|masked)
                echo "$detected_systemd_state"
                return
                ;;
        esac
    fi

    if systemd_links_enabled; then
        echo enabled
        return
    fi

    if [ "$detection_mode" = allow-unknown ]; then
        case "$detected_systemd_state" in
            enabled|disabled)
                echo "$detected_systemd_state"
                return
                ;;
        esac
        echo unknown
        return
    fi

    echo disabled
}

register_legacy_init()
{
    desired_state=$1

    if [ -n "$compat_root" ]; then
        echo "Registering legacy xcatd is not supported with XCAT_COMPAT_ROOT" >&2
        return 2
    fi

    if [ -x /sbin/chkconfig ]; then
        /sbin/chkconfig --add xcatd || return
        case "$desired_state" in
            default) : ;;
            enabled) /sbin/chkconfig --level 345 xcatd on ;;
            disabled) /sbin/chkconfig xcatd off ;;
        esac
    elif { [ "$desired_state" = default ] || [ "$desired_state" = enabled ]; } &&
         [ -x /usr/lib/lsb/install_initd ]; then
        /usr/lib/lsb/install_initd /etc/init.d/xcatd
    elif [ "$desired_state" = disabled ] && [ -x /usr/lib/lsb/remove_initd ]; then
        # The LSB fallback has no portable register-but-disabled operation.
        /usr/lib/lsb/remove_initd /etc/init.d/xcatd
    else
        echo "Unable to register init scripts on this system"
    fi
}

unregister_legacy_init()
{
    if [ -z "$compat_root" ] &&
       { [ -e "$legacy_init" ] || [ -L "$legacy_init" ]; }; then
        if [ -x /sbin/chkconfig ]; then
            /sbin/chkconfig --del xcatd || true
        elif [ -x /usr/lib/lsb/remove_initd ]; then
            /usr/lib/lsb/remove_initd /etc/init.d/xcatd || true
        fi
    fi

    rm -f \
        "$compat_root"/etc/rc.d/rc?.d/[SK]??xcatd \
        "$compat_root"/etc/rc?.d/[SK]??xcatd \
        "$compat_root"/etc/init.d/rc?.d/[SK]??xcatd
}

disable_systemd_init()
{
    disable_mode=${1:-host-tools}

    if [ "$disable_mode" != links-only ] &&
       [ -z "$compat_root" ] && command -v systemctl >/dev/null 2>&1; then
        systemctl disable xcatd.service >/dev/null 2>&1 || true
    fi

    # Preserve administrator masks while removing only boot enablement links.
    rm -f \
        "$compat_root"/etc/systemd/system/*.wants/xcatd.service \
        "$compat_root"/etc/systemd/system/*.requires/xcatd.service \
        "$compat_root"/run/systemd/system/*.wants/xcatd.service \
        "$compat_root"/run/systemd/system/*.requires/xcatd.service
}

unregister_all_init()
{
    unregister_status=0
    disable_systemd_init || unregister_status=$?
    unregister_legacy_init || unregister_status=$?
    return "$unregister_status"
}

legacy_init_is_managed()
{
    [ -f "$managed_marker" ] &&
        [ -f "$legacy_init" ] && [ ! -L "$legacy_init" ] &&
        [ -r "$legacy_template" ] && cmp -s "$legacy_template" "$legacy_init"
}

mark_legacy_init_managed()
{
    marker_dir=${managed_marker%/*}
    marker_tmp="$managed_marker.tmp.$$"
    mkdir -p -m 0755 "$marker_dir" || return 1
    umask 077
    : > "$marker_tmp" || return 1
    if ! mv -f "$marker_tmp" "$managed_marker"; then
        rm -f "$marker_tmp"
        return 1
    fi
}

remove_legacy_init()
{
    rm -f "$legacy_init" || return 1
    rm -f "$managed_marker"
}

remove_managed_legacy_init()
{
    if legacy_init_is_managed; then
        rm -f "$legacy_init" || return 1
    fi
    rm -f "$managed_marker"
}

configure_legacy_init()
{
    replace_existing=0
    detection_mode=compat
    track_managed=0
    for configure_option in "$@"; do
        case "$configure_option" in
            --replace) replace_existing=1 ;;
            --explicit-target) detection_mode=explicit ;;
            --track-managed) track_managed=1 ;;
            *)
                echo "Usage: $0 configure [--replace] [--explicit-target] [--track-managed]" >&2
                exit 2
                ;;
        esac
    done

    if uses_systemd "$detection_mode"; then
        rm -f "$legacy_init" || return 1
        rm -f "$managed_marker" || return 1
        return
    fi

    # A broken link cannot provide a usable init script.  Replace it while
    # preserving links whose administrator-managed target still exists.
    if [ -L "$legacy_init" ] && [ ! -e "$legacy_init" ]; then
        rm -f "$legacy_init" || return 1
    fi

    if [ -e "$legacy_init" ] || [ -L "$legacy_init" ]; then
        if [ "$replace_existing" -ne 1 ]; then
            if [ -f "$managed_marker" ] && ! legacy_init_is_managed; then
                rm -f "$managed_marker" || return 1
            fi
            return
        fi
        rm -f "$legacy_init" || return 1
    fi

    if [ ! -r "$legacy_template" ]; then
        echo "Legacy xcatd init template not found: $legacy_template" >&2
        exit 1
    fi

    mkdir -p "$compat_root/etc/init.d" || return 1
    cp "$legacy_template" "$legacy_init" || return 1
    chmod 755 "$legacy_init" || return 1
    if [ "$track_managed" -eq 1 ]; then
        mark_legacy_init_managed || return 1
    else
        rm -f "$managed_marker" || return 1
    fi
}

case "${1:-}" in
    uses-systemd)
        detection_mode=compat
        case "${2:-}" in
            '') ;;
            --explicit-target) detection_mode=explicit ;;
            *)
                echo "Usage: $0 uses-systemd [--explicit-target]" >&2
                exit 2
                ;;
        esac
        uses_systemd "$detection_mode"
        ;;
    can-use-systemctl)
        case "${2:-}" in
            '') can_use_systemctl ;;
            *)
                echo "Usage: $0 can-use-systemctl" >&2
                exit 2
                ;;
        esac
        ;;
    legacy-state)
        legacy_state
        ;;
    debian-legacy-state)
        if [ "$#" -ne 1 ]; then
            echo "Usage: $0 debian-legacy-state" >&2
            exit 2
        fi
        legacy_state debian-layout
        ;;
    legacy-transition-state)
        legacy_transition_state
        ;;
    systemd-state)
        if [ "$#" -eq 2 ] && [ "${2:-}" = --allow-unknown ]; then
            systemd_state allow-unknown
        else
            systemd_state
        fi
        ;;
    register-legacy)
        case "${2:-}" in
            default|enabled|disabled) ;;
            *)
                echo "Usage: $0 register-legacy {default|enabled|disabled}" >&2
                exit 2
                ;;
        esac
        register_legacy_init "$2"
        ;;
    unregister-legacy)
        unregister_legacy_init
        ;;
    disable-systemd)
        case "${2:-}" in
            '') disable_systemd_init ;;
            --links-only) disable_systemd_init links-only ;;
            *)
                echo "Usage: $0 disable-systemd [--links-only]" >&2
                exit 2
                ;;
        esac
        ;;
    unregister-all)
        unregister_all_init
        ;;
    configure)
        shift
        configure_legacy_init "$@"
        ;;
    remove)
        remove_legacy_init
        ;;
    remove-managed)
        remove_managed_legacy_init
        ;;
    *)
        echo "Usage: $0 {uses-systemd [--explicit-target]|can-use-systemctl|legacy-state|debian-legacy-state|legacy-transition-state|systemd-state [--allow-unknown]|register-legacy {default|enabled|disabled}|unregister-legacy|disable-systemd [--links-only]|unregister-all|configure [--replace] [--explicit-target] [--track-managed]|remove|remove-managed}" >&2
        exit 2
        ;;
esac
