#!/bin/bash
# Copyright (c) 2005 Novell Inc.
# All rights reserved.
#
# Author: Charles Coffing
# Please send feedback to http://www.suse.de/feedback/
#
# /etc/init.d/unh_iscsi_target
#
### BEGIN INIT INFO
# Provides:           unh-iscsi-target
# Required-Start:     $network
# Should-Start:       
# Required-Stop:      $network
# Should-Stop:        
# Default-Start:      3 5
# Default-Stop:       0 1 2 4 6
# Short-Description:  UNH iSCSI target
# Description:        The UNH iSCSI target implements the IETF
#	iSCSI SAN protocol in software
### END INIT INFO

. /etc/rc.status
rc_reset

UNH_SCSI=unh_scsi_target
UNH_ISCSI=unh_iscsi_target
UNH_ISCSI_BIN=/sbin
UNH_ISCSI_CONF=/etc/unh_iscsi_target
UNH_ISCSI_LOG=/var/log/unh_iscsi_target.log
UNH_DISKDIR=/srv/iscsi/
KERNEL_VER="`/bin/uname -r`"

unh_log() {
    echo "$*" >> ${UNH_ISCSI_LOG}
}

check_loaded() {
    LOADED="`lsmod | grep -w ${1}`"
    [ ! -z "$LOADED" ] && return 0
    return 3  # LSB-defined value
}

mod_in_use() {
    usage=`lsmod | eval grep \"^${1}\" | awk '{print $3;}'`
    [ "0${usage}" -gt 0 ] && return 0
    return 1
}

load_module() {
    MOD_DIR=/lib/modules/${KERNEL_VER}/extra
    MOD_OBJ=${MOD_DIR}/${1}.ko
    unh_log "insmod ${MOD_OBJ}"
    insmod ${MOD_OBJ} >> ${UNH_ISCSI_LOG} 2>&1
    [ $? -eq 0 ] && return 0
    return 1
}

unload_module() {
    MODULE=$1

    check_loaded $MODULE
    if [ $? -eq 0 ]; then
        rmmod ${MODULE} >> ${UNH_ISCSI_LOG} 2>&1
        return $?
    fi

    return 0
}

unh_iscsi_start() {
    local error=0

    DATE="`date`"
    mv -f ${UNH_ISCSI_LOG} ${UNH_ISCSI_LOG}.old > /dev/null 2>&1
    unh_log "UNH iSCSI target loading at ${DATE} on Linux kernel ${KERNEL_VER}"

    check_loaded ${UNH_ISCSI}
    [ $? -eq 0 ] && return 0

    # The module creates disks in the CWD, as of when it was loaded. Yuck!
    mkdir -p $UNH_DISKDIR
    cd $UNH_DISKDIR

    load_module ${UNH_SCSI}
    [ $? -eq 0 ] || return $?
    load_module ${UNH_ISCSI}
    if [ $? -ne 0 ]; then
        unload_module ${UNH_SCSI}
        return 1
    fi

    if [ ! -f ${UNH_ISCSI_CONF}/exports ]; then
        return 0
    fi

    # Remove old devices
    lines="`cat /proc/scsi_target/scsi_target`"
    echo "$lines" | while read line; do
        command=`echo "$line" | sed -ne 's/^iscsi target \([0-9][0-9]*\) lun \([0-9][0-9]*\).*/delfile targetid=\1 lun=\2/p'`
        [ -z "$command" ] && continue
        echo "$command" > /proc/scsi_target/scsi_target
    done

    # Add new devices
    while read TID LUN FILENAME SCRIPT; do
        echo $TID | grep -q '^[ \t]*#' && continue
        [ -z "${TID}" ] && continue

        if [ "${TID//[0-9]}" != "" ] ||
           [ "${LUN//[0-9]}" != "" ]; then
            unh_log "error in export file: TID or LUN is not a number"
            error=1
            break
        fi
        if [ -z "${FILENAME//\/}" ]; then
            unh_log "error in export file: FILENAME was not specified"
            error=1
            break
        fi
        if [ "${FILENAME}" == "${FILENAME/\/}" ]; then
            FILENAME="/srv/iscsi/${FILENAME}"
        fi
        if [ ! -e "${FILENAME}" ]; then
            unh_log "error in export file: '${FILENAME}' does not exist"
            error=1
            break
        fi

        if [ ! -z "${SCRIPT}" ]; then
            if [ ! -f "${SCRIPT}" ]; then
                unh_log "error in export file: '${SCRIPT}' does not exist"
                error=1
                break
            fi
            "${SCRIPT}" ${TID} ${LUN} "${FILENAME}"
            continue
        fi

        if [ -b "$FILENAME" ] ; then
            dev=`basename "$FILENAME"`
            part=$dev
            dev=${dev/[0-9]*/}
            [ "$part" == "$dev" ] && part=""
            sys="/sys/block/$dev/$part/size"
            BLOCKS=`cat $sys 2>&1` >> ${UNH_ISCSI_LOG}
            if [ $? -ne 0 ]; then
                unh_log "Failed to query the size of '$FILENAME' block device."
                error=1
                break
            fi
        else
            BLOCKS=`stat --format="%s" "${FILENAME}" 2>&1` >> ${UNH_ISCSI_LOG}
            if [ $? -ne 0 ]; then
                unh_log "Failed to query size of '$FILENAME' file."
                error=1
                break
            fi
            BLOCKS=$(( (BLOCKS+511) / 512 ))
        fi

        unh_log "Exporting target: $TID $LUN $FILENAME $SCRIPT"

        echo "addfile targetid=$TID lun=$LUN filename=$FILENAME blocksinfile=$BLOCKS blocksize=512" > /proc/scsi_target/scsi_target
    done < ${UNH_ISCSI_CONF}/exports
    [ $error -eq 0 ] || unh_iscsi_stop
    return $error
}

unh_iscsi_stop() {
    mod_in_use ${UNH_ISCSI}
    if [ $? -eq 0 ]; then
        unh_log "ERROR: Module can not be unloaded while in use."
        return 1
    fi

    # Don't "&&" these return codes -- maybe $UNH_ISCSI is already
    # unloaded for some reason, but I still want to finish cleaning up.
    unload_module $UNH_ISCSI
    unload_module $UNH_SCSI
    
    if [ $? -eq 0 ]; then
        unh_log "Module unloaded successfully."
        return 0
    fi

    unh_log "ERROR: Module could not be unloaded."
    return 1
}

case "$1" in
    start)
        echo -n "Starting UNH iSCSI target "
        unh_iscsi_start
        rc_status -v
        ;;
    stop)
        echo -n "Shutting down UNH iSCSI target " 
        unh_iscsi_stop
        rc_status -v
        ;;
    restart|force-reload)
        $0 stop
        $0 start
        rc_status
        ;;
    try-restart)
        check_loaded ${UNH_ISCSI}
        if [ $? -eq 0 ]; then
            $0 restart
        else
            rc_reset
        fi
        rc_status
        ;;
    reload)
        # Unsupported
        rc_failed 3
        rc_status -v
        ;;
    status)
        echo -n "Checking for UNH iSCSI target "
        check_loaded ${UNH_ISCSI}
        rc_status -v
        ;;
    *)
        echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload}"
        exit 1
        ;;
esac
rc_exit

