#!/bin/bash
#
# /etc/init.d/iscsi
#
### BEGIN INIT INFO
# Provides:          iscsi
# Required-Start:    $network
# Should-Start:      iscsitarget
# Required-Stop:     
# Should-Stop:
# Default-Start:     3 5
# Default-Stop:      
# Short-Description: Starts and stops the iSCSI client initiator
#                    
### END INIT INFO

PID_FILE=/var/run/iscsi.pid
CONFIG_FILE=/etc/iscsi/iscsid.conf
DAEMON=/sbin/iscsid
ISCSIADM=/sbin/iscsiadm
ARGS="-c $CONFIG_FILE -p $PID_FILE"

# Source LSB init functions
. /etc/rc.status

# Reset status of this service
rc_reset

dm_major()
{
    local maj

    [ -f /proc/devices ] || echo 0
    maj=$(sed -n 's/\([0-9]*\) device-mapper/\1/p' /proc/devices)
    echo $maj
}

umount_lun()
{
    local dev=$1
    local d m t l

    cat /proc/mounts | sed -ne '/^\/dev\/.*/p' | while read d m t o x; do 
	if [ "$m" = "/" ] ; then 
	    continue;
	fi
	l=$d
	[ -L "$d" ] && l=$(readlink -f $d)

	if [ "${l##/dev/mapper}" != "$l" ] ; then
	    t=$(dmsetup info -c --noheadings -o minor ${l##/dev/mapper/})
	    l="/dev/dm-$t"
	fi

	if [ "$l" != "$dev" ] ; then
	    continue;
	fi

	umount $d
	break;
    done
}

teardown_md()
{
    local dev=${1##/dev/}
    local md_minor

    md_minor=$(cat /proc/mdstat | sed -n "s/\(md[0-9]*\) : .* $dev.*/\1/p")

    if [ "$md_minor" ] ; then
	teardown_iscsi /dev/${md_minor}
	mdadm -S /dev/${md_minor}
	return 0
    fi

    return 1
}

teardown_dm()
{
    local dev=${1##/dev/}
    local dm_maj=$(dm_major)
    local dm_min
    local retval=1
    local tblname

    read dev_t < /sys/block/$dev/dev

    dmsetup table | sed -n "s/\(.*\): .* $dev_t .*/\1/p" | while read tblname; do
	tbluuid=$(dmsetup info -c --noheadings -o uuid $tblname)
	[ -z "$tbluuid" ] && continue
	dm_min=$(dmsetup info -c --noheadings -o minor $tblname)
	if [ ${tbluuid##mpath-} != "$tbluuid" ] ; then
	    teardown_iscsi /dev/dm-$dm_min
	    dmsetup remove $tblname
	    retval=0
	elif [ ${tbluuid##part} != "$tbluuid" ] ; then
	    teardown_iscsi /dev/dm-$dm_min
	    dmsetup remove $tblname
	    retval=0
	elif [ ${tbluuid##LVM-} != "$tbluuid" ] ; then
	    # We don't support stacking atop of LVM devices
	    umount_lun /dev/dm-$dm_min
	    dmsetup remove $tblname
	    retval=0
	fi
    done
    return $retval
}

teardown_block()
{
    local dev=${1##/dev}
    local s
    local p

    if [ ${dev##/sd} == $dev ] ; then
	return 1
    fi

    p="/sys/block${dev%%[0-9]*}"

    if [ ! -d ${p} ] && [ ! -d ${p}/device ] ; then
	continue;
    fi

    s=$(cd -P ${p}/device && echo $PWD)

    case "$s" in
	*/session[0-9]*/*)
	    # This is an iSCSI device
            umount_lun $dev
	    return 0
	    ;;
    esac
    return 1
}

teardown_iscsi()
{
    local dev=$1

    teardown_md $dev && return

    teardown_dm $dev && return

    teardown_block $dev && return

    umount_lun $dev
}

iscsi_umount_all_luns()
{
    local d m dev p s

    for target in /sys/class/iscsi_session/session*/device/target* ; do
	[ -e $target ] || continue;
	cd -P $target;
	for lun in *:*:*:*; do
	    cd -P /sys/class/scsi_device/$lun/device
	    for l in block:* ; do
		[ -e $l ] || continue
		dev=${l##block:}
		teardown_iscsi /dev/$dev
	    done
	done
    done
}

iscsi_login_all_nodes()
{
	echo -n "Setting up iSCSI targets: "
	$ISCSIADM -m node --loginall=automatic 2> /dev/null
	if [ $? == 19 ] ; then
	    rc_failed 6
	fi
	rc_status -v
}

iscsi_logout_all_nodes()
{
	echo -n "Closing all iSCSI connections: "
	# Logout from all sessions marked automatic
	if ! $ISCSIADM -m node --logoutall=automatic 2> /dev/null; then
		if [ $? == 19 ] ; then
		    RETVAL=6
		else
		    RETVAL=1
		fi
		rc_failed $RETVAL
	fi
	rc_status -v

	# Not sure whether this is still needed
	sleep 1
	return ${RETVAL:-0}
}

iscsi_list_all_nodes()
{
    # Check for active sessions
    if $ISCSIADM -m session > /dev/null; then
	return 0
    fi
    echo "Active connections:"
    $ISCSIADM -m session | while read proto num PORTAL TARGET ; do
	PORTAL=${PORTAL%,*}
	echo -e "\t$TARGET at $PORTAL"
    done
}

iscsi_discover_all_targets()
{
	# Strip off any existing ID information
	RAW_NODE_LIST=`iscsiadm -m node | sed -nre 's/^(\[[0-9a-f]*\] )?(.*)$/\2/p'`
	# Obtain IPv4 list
	IPV4_NODE_LIST=`echo "$RAW_NODE_LIST" | sed -nre 's/^([0-9]{1,3}(\.[0-9]{1,3}){3}):[^: ]* (.*)$/\1 \3/p'`
	# Now obtain IPv6 list
	IPV6_NODE_LIST=`echo "$RAW_NODE_LIST" | sed -nre 's/^([0-9a-f]{1,4}(:[0-9a-f]{0,4}){6}:[0-9a-f]{1,4}):[^: ]* (.*)$/\1 \3/p'`

	DISC_TARGETS=""
	while read NODE_ADDR NODE_NAME; do
		[ -z "$NODE_ADDR" -a -z "$NODE_NAME" ] && continue
		NODE_ATTRS=`iscsiadm -m node -p "$NODE_ADDR" -T "$NODE_NAME"`
		NODE_STATUS=`echo "$NODE_ATTRS" | sed -nre 's/^.*node\.conn\[0\]\.startup = ([a-z]*).*$/\1/p'`

		if [ "$NODE_STATUS" == 'automatic' ]; then
			DISC_TARGETS=`echo "$DISC_TARGETS" | sed -re '/'"$NODE_ADDR"'/!{s/(.*)/\1 '"$NODE_ADDR"'/}'`
		fi
	done < <(echo "$IPV4_NODE_LIST"; echo "$IPV6_NODE_LIST")

	for TARGET_ADDR in $DISC_TARGETS; do
		echo -n "Attempting discovery on target at ${TARGET_ADDR}: "
		iscsiadm -m discovery -t st -p "$TARGET_ADDR" > /dev/null 2>&1
		if [ "$?" -ne 0 ]; then
			rc_failed 1
			rc_status -v
			return 1
		fi
		rc_status -v
	done
}

case "$1" in
    start)
	[ ! -d /var/lib/iscsi ] && mkdir -p /var/lib/iscsi
	if checkproc $DAEMON ; then
	    RETVAL=0
	else
	    echo -n "Starting iSCSI initiator service: "
	    modprobe iscsi_tcp
	    modprobe -q ib_iser
	    startproc $DAEMON $ARGS
	    RETVAL=$?
	    rc_status -v
	fi
	if [ "$RETVAL" == "0" ]; then
	    iscsi_discover_all_targets
	    RETVAL=$?
	fi
	if [ "$RETVAL" == "0" ]; then
	    iscsi_login_all_nodes
	fi
	;;
    stop)
	iscsi_umount_all_luns
	if iscsi_logout_all_nodes ; then
	    killproc -KILL $DAEMON
	    RETVAL=$?
	else
	    RETVAL=1
	fi
	echo -n "Stopping iSCSI initiator service: "
	if [ "$RETVAL" == "0" ]; then
	    rm -f $PID_FILE
	    status=0
	    modprobe -r iscsi_tcp
	    if [ "$?" -ne "0" -a "$?" -ne "1" ]; then
	    	status=1
	    fi
	    modprobe -q -r ib_iser
            if [ "$?" -ne "0" -a "$?" -ne "1" ]; then
	        status=1 
            fi
	    rc_failed $status
	else
	    rc_failed $RETVAL
	fi
	rc_status -v
	;;
    status)
	echo -n "Checking for iSCSI initiator service: "
	if checkproc $DAEMON ; then
	    rc_status -v
	    iscsi_list_all_nodes
	else
	    rc_failed 3
	    rc_status -v
	fi
	;;
    restart)
	$0 stop
	sleep 1
	$0 start
	;;
    *)
	echo "Usage: $0 {start|stop|status|restart}"
	exit 1
	;;
esac
rc_exit

