#! /bin/sh
### BEGIN INIT INFO
# Provides:          target
# Required-Start:    $network $remote_fs $syslog
# Required-Stop:     $network $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: The Linux SCSI Target service
### END INIT INFO

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="The Linux SCSI Target"
NAME=target
DAEMON=/usr/bin/targetcli
DAEMON_ARGS=""
SCRIPTNAME=/etc/init.d/$NAME

CFS_BASE="/sys/kernel/config"
CFS_TGT="${CFS_BASE}/target"
CORE_MODS="target_core_mod target_core_pscsi target_core_iblock target_core_file"
STARTUP_CONFIG="/etc/target/scsi_target.lio"

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# How we log messages depends on the system
if [ -r /lib/lsb/init-functions ]; then
    # LSB systems like Debian
    . /lib/lsb/init-functions
elif [ -r /etc/init.d/functions ]; then
    # RHEL without optional redhat-lsb
    . /etc/init.d/functions
fi

is_func() {
    type $1 2>/dev/null | grep -q 'function'
}

log_success () {
    if is_func log_success_msg; then
        log_success_msg "$*"
    elif is_func success; then
        echo -n $*; success "$*"; echo
    else
        echo "[ ok ] $*"
    fi
}

log_failure () {
    if is_func log_failure_msg; then
        log_failure_msg "$*"
    elif is_func failure; then
        echo -n $*; failure "$*"; echo
    else
        echo "[FAIL] $* ... failed!"
    fi
}

log_warning () {
    if is_func log_warning_msg; then
        log_warning_msg "$*"
    elif is_func warning; then
        echo -n $*; warning "$*"; echo
    else
        echo "[warn] $* ... (warning)."
    fi
}

log_action () {
    if is_func log_action_msg; then
        log_action_msg "$*"
    elif is_func action; then
        echo -n $*; passed "$*"; echo
    else
        echo "[info] $*."
    fi
}

load_specfiles()
{
FABRIC_MODS=$(python << EOF
from rtslib import list_specfiles, parse_specfile
print(" ".join(["%(kernel_module)s:%(configfs_group)s" % parse_specfile(spec)
                for spec in list_specfiles()]))
EOF
)
}

save_running_config()
{
python << EOF
import rtslib
config = rtslib.Config()
config.load_live()
config.save("${STARTUP_CONFIG}")
EOF
}

check_install()
{
    # Check the system installation
    INSTALL=ok

    python -c "from rtslib import Config" > /dev/null 2>&1
    if [ $? != 0 ]; then
        log_failure "Cannot load rtslib"
        INSTALL=nok
    fi

    SYSTEM_DIRS="/var/target/pr /var/target/alua /etc/target"
    for DIR in ${SYSTEM_DIRS}; do
        if [ ! -d ${DIR} ]; then
            log_warning "Creating missing directory ${DIR}"
            mkdir -p ${DIR}
        fi
    done

    if [ "${INSTALL}" != ok ]; then
        exit 0
    else
        log_action "${DESC} looks properly installed"
    fi
}

load_configfs()
{
    modprobe configfs > /dev/null 2>&1
    if [ "$?" != 0 ]; then
        log_failure "Failed to load configfs kernel module"
        return 1
    fi
    mount -t configfs configfs ${CFS_BASE} > /dev/null 2>&1
    case "$?" in
        0) log_warning "The configfs filesystem was not mounted, consider adding it to fstab";;
        32) log_action "The configfs filesystem is already mounted";;
        *) log_failure "Failed to mount configfs"; return 1;;
    esac
}

load_modules()
{
    for MODULE in ${CORE_MODS}; do
        if [ ! -z "$(cat /proc/modules | grep ^${MODULE}\ )" ]; then
            log_warning "Core module ${MODULE} already loaded"
        else
            modprobe "${MODULE}" > /dev/null 2>&1
            if [ "$?" != 0 ]; then
                log_failure "Failed to load core module ${MODULE}"
                return 1
            else
                log_action "Loaded core module ${MODULE}"
            fi
        fi
    done
    for MOD_SPEC in ${FABRIC_MODS}; do
        MODULE="$(echo ${MOD_SPEC} | awk -F : '{print $1}')"
        if [ ! -z "$(cat /proc/modules | grep ^${MODULE}\ )" ]; then
            log_warning "Fabric module ${MODULE} already loaded"
        else
            modprobe "${MODULE}" > /dev/null 2>&1
            if [ "$?" != 0 ]; then
                log_warning "Failed to load fabric module ${MODULE}"
            else
                log_action "Loaded fabric module ${MODULE}"
            fi
        fi
    done
}

unload_modules()
{
    RETCODE=0

    for MOD_SPEC in ${FABRIC_MODS}; do
        MODULE="$(echo ${MOD_SPEC} | awk -F : '{print $1}')"
        CFS_GROUP="${CFS_TGT}/$(echo ${MOD_SPEC} | awk -F : '{print $2}')"
        if [ ! -z "$(lsmod | grep ^${MODULE}\ )" ]; then
            rmdir "${CFS_GROUP}" > /dev/null 2>&1
            if [ -d "${CFS_GROUP}" ]; then
                log_failure "Failed to remove ${CFS_GROUP}"
                RETCODE=1
            else
                rmmod "${MODULE}" > /dev/null 2>&1
                if [ "$?" != 0 ]; then
                    log_failure "Failed to unload fabric module ${MODULE}"
                    RETCODE=1
                else
                    log_action "Unloaded ${MODULE} fabric module"
                fi
            fi
        else
            log_warning "Fabric module ${MODULE} is not loaded"
        fi
    done

    MODULES="$(echo ${CORE_MODS} | tac -s ' ')"
    for MODULE in ${MODULES}; do
        if [ ! -z "$(lsmod | grep ^${MODULE}\ )" ]; then
            rmmod "${MODULE}" > /dev/null 2>&1
            if [ "$?" != 0 ]; then
                log_failure "Failed to unload target core module ${MODULE}"
                RETCODE=1
            else
                log_action "Unloaded ${MODULE} target core module"
            fi
        else
            log_warning "Target core module ${MODULE} is not loaded"
        fi
    done

    return "${RETCODE}"
}

load_config()
{

if [ $(cat /etc/target/scsi_target.lio 2>/dev/null | tr -d " \n\t" | wc -c) = 0 ]; then
    log_warning "Startup config ${STARTUP_CONFIG} is empty, skipping"
elif [ -e "${STARTUP_CONFIG}" ]; then
export __STARTUP_CONFIG="${STARTUP_CONFIG}"
python 2> /dev/null << EOF
import os, rtslib
config = rtslib.Config()
config.load(os.environ['__STARTUP_CONFIG'], allow_new_attrs=True)
list(config.apply())
EOF
    if [ "$?" != 0 ]; then
        unset __STARTUP_CONFIG
        log_failure "Failed to load ${STARTUP_CONFIG}"
        return 1
    else
        unset __STARTUP_CONFIG
        log_action "Loaded ${STARTUP_CONFIG}"
    fi
else
    log_warning "No ${STARTUP_CONFIG} to load"
fi
}

clear_config()
{
python 2> /dev/null << EOF
from rtslib import Config
config = Config()
list(config.apply())
EOF

if [ "$?" != 0 ]; then
    log_failure "Failed to clear configuration"
    return 1
else
    log_action "Cleared configuration"
fi
}

do_start()
{
    # If the target is running and we do not have a config file on the
    # system, dump the running system config to the config file. This
    # helps migrating away from lio-utils or other legacy/devel systems.
    if [ ! -e "${STARTUP_CONFIG}" ] && [ $(ls /sys/kernel/config/target/core/ 2>/dev/null | wc -l) -gt 1 ]; then
        log_action "Possible config migration detected, saving the " \
            "running target to ${STARTUP_CONFIG}"
        save_running_config
    elif [ -d  ${CFS_TGT} ]; then
        log_failure "Not starting: ${CFS_TGT} already exists"
        return 1
    fi

    load_specfiles # Fill in FABRIC_MODS and CFS_GROUPS
    check_install && load_configfs && load_modules && load_config
    if [ "$?" != 0 ]; then
        log_failure "Could not start ${DESC}"
        return 1
    else
        log_success "Started ${DESC}"
    fi
}

do_stop()
{
    if [ ! -d ${CFS_TGT} ]; then
        log_success "${DESC} is already stopped"
    else
        load_specfiles # Fill in FABRIC_MODS and CFS_GROUPS
        clear_config && unload_modules
        if [ "$?" != 0 ]; then
            log_failure "Could not stop ${DESC}"
            return 1
        else
            log_success "Stopped ${DESC}"
        fi
    fi
}

do_status()
{
    if [ -d ${CFS_TGT} ]; then
        log_action "${DESC} is started"
        return 0
    else
        log_action "${DESC} is stopped"
        return 1
    fi
}

case "$1" in
    start)
        # FIXME This is because stop fails with systemd on debian jessie
	    do_stop
	    do_start
        ;;
    stop)
	    do_stop
        ;;
    status)
        do_status ;;
    restart|force-reload)
	    do_stop && do_start ;;
    *)
	    echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
	    exit 3
	    ;;
esac
