#!/bin/bash
#
# /etc/init.d/iscsi
#
### BEGIN INIT INFO
# Provides:          iscsi
# Required-Start:    $network
# Should-Start:      iscsitarget
# Required-Stop:     $network
# Should-Stop:       $null
# Default-Start:     3 5
# Default-Stop:      
# Short-Description: iSCSI initiator daemon
# Description:       The iSCSI initator is used to create and
#                    manage iSCSI connections to a iSCSI Target.
#                    
### END INIT INFO

CONFIG_FILE=/etc/iscsi/iscsid.conf
DAEMON=/sbin/iscsid
ISCSIADM=/sbin/iscsiadm
BRCM_ISCSIUIO=/sbin/brcm_iscsiuio
ARGS="-c $CONFIG_FILE -n"

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

# Reset status of this service
rc_reset

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
}

#
# Try to load all required modules prior to startup
#
iscsi_load_transport_modules()
{
    loaded=$(sed -n "/^iscsi_tcp/p" /proc/modules)
    if [ -z "$loaded" ] ; then
	modprobe iscsi_tcp
	if [ $? = 0 ] ; then
	    echo -n " tcp"
	fi
    fi

    for iface in /etc/iscsi/ifaces/*; do
	[ -f "$iface" ] || continue
	[ "$iface" = "iface.example" ] && continue
	# Check if the iface has been configured
	result=$(sed '/#.*/D;/iface.iscsi_ifacename/D;/iface.hwaddress/D;/iface.transport_name/D' $iface)
	if [ "$result" ] ; then
	    mod=$(sed -n 's/iface.transport_name *= *\(.*\)/\1/p' $iface)
	    loaded=$(sed -n "/^$mod/p" /proc/modules)
	    if [ -z "$loaded" ] ; then
		modprobe $mod
		if [ $? = 0 ] ; then
		    echo -n " $mod"
		fi
	    fi
	fi
    done
}

#
# Set a temporary startmode for ifdown
#
iscsi_modify_if_startmode()
{
    local ifname=$1
    local tmp_ifcfg=/dev/.sysconfig/network/if-$ifname

    if [ -e "$tmp_ifcfg" ] ; then
	. $tmp_ifcfg
	if [ "$startmode" ] ; then
	    return
	fi
    fi
    echo "disabling shutdown on $ifname"
    echo "startmode=nfsroot" >> $tmp_ifcfg
}

iscsi_get_ifacename_from_session()
{
    local session=$1
    local ifacename

    ifacename=$(iscsiadm -m session -r ${session##.*/session} 2> /dev/null | \
	sed -n 's/iface.iscsi_ifacename = \(.*\)/\1/p')

    echo $ifacename
}

iscsi_get_hwaddress_from_iface()
{
    local iface=$1
    local hwaddress

    hwaddress=$(iscsiadm -m iface -I "$ifacename" 2> /dev/null | sed -n 's/iface.hwaddress = \(.*\)/\1/p')

    echo $hwaddress
}

#
# cxgb3i is using the HWAddress to select
# the correct interface
#
iscsi_get_ifname_from_hwaddress()
{
    local hwaddress=$1

    for if in /sys/class/net/*; do
	[ -e "$if" ] || continue
	read mac < $if/address
	[ "$mac" = "$hwaddress" ] || continue
	echo ${if##*/}
	break
    done
}

#
# Handle 'default' interface:
# It is basically impossible to determine via which
# interface the iSCSI traffic will flow, so we take
# the easy option and ignore _all_ active interfaces
# during shutdown
#
iscsi_modify_all_interfaces()
{
    ip link show up | sed -n '/.*LOOPBACK.*/d;s/[0-9]*: \(.*\): .*/\1/p' | while read ifname; do
	iscsi_modify_if_startmode $ifname
    done
}

#
# Check iface setting and disable
# affected network interfaces
#
iscsi_check_interface()
{
    local session=$1
    local i h n

    i=$(iscsi_get_ifacename_from_session $session)
    [ -z "$i" ] && continue
    if [ "$i" = "default" ] ; then
	iscsi_modify_all_interfaces
    else
	h=$(iscsi_get_hwaddress_from_iface $i)
	if [ -n "$h" ] ; then
	    n=$(iscsi_get_ifname_from_hwaddress $h)
	    iscsi_modify_if_startmode $n
	fi
    fi
}

#
# Check if device 'd' matches device
# 'm' or one if its slaves
#
iscsi_check_device()
{
    local p=$1
    local m=$2
    local d=$3

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

    if [ "${p##*/}" = "$d" ] ; then
	echo "$m"
	return 0
    fi
    for s in $p/slaves/* ; do
	[ -e $s ] || continue
	iscsi_check_device $s $m $d
    done

}

#
# Check if device 'dev' is mounted
#
iscsi_check_mounts()
{
    local dev=$1
    local d m t o x p

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

	b=$(ls -l $d | sed -n 's/.* \([0-9]*\), \([0-9]*\) .*/\1:\2/p')
	p=$(cd -P /sys/dev/block/$b ; echo $PWD)

	if [ -z "$p" ] ; then
	    d=${d##/dev}
	    p="/sys/block${d%%[0-9]*}"
	fi

	[ ! -d ${p} ] && continue

	if [ -e $p/partition ] ; then
	    p=$(cd -P $p/../; echo $PWD)
	fi

	iscsi_check_device $p ${p##*/} $dev
    done
}

#
# Return all targets for a given session
#
iscsi_get_target()
{
    local session=$1
    local d

    for d in $session/device/target* ; do
	[ -e "$d" ] || continue
	echo "$d"
    done
}

#
# Check if a device presented by a target
# is mounted and try to umount it.
# Return the mount point if the call to
# umount failed.
#
iscsi_check_target()
{
    local t=$1
    local d b dev mnt

    for d in $t/* ; do
	[ -d $d/block ] || continue
	for b in $d/block/sd* ; do
	    [ -d "$b" ] || continue
	    dev=${b##*/}
	    mnt=$(iscsi_check_mounts $dev)
	    [ -z "$mnt" ] && continue
	    case "$mnt" in
		rootfs)
		    echo $mnt
		    return 1
		    ;;
		dm-*)
		    echo $mnt
		    return 1
		    ;;
		md*)
		    echo $mnt
		    return 1
		    ;;
		*)
		    umount $mnt 2> /dev/null
		    if [ $? -ne 0 ] ; then
			echo "Failed to umount $mnt"
			echo $mnt
			return 1
		    fi
		    ;;
	    esac
	done
    done
}

#
# Check all sessions for mounted devices
# and shutdown the session if the affected
# devices could be umounted cleanly.
# If umount fails disable shutdown on all
# affected network interfaces
#
iscsi_stop_sessions()
{
    local t m s

    for session in /sys/class/iscsi_session/session* ; do
	[ -e "$session" ] || continue;
	[ -e $session/device ] || continue
	t=$(iscsi_get_target $session)
	m=$(iscsi_check_target $t)
	s=${session##*/session}
	if [ -z "$m" ] ; then
	    echo "stopping session $s"
	    iscsiadm -m session -r ${s} -u
	else
	    iscsi_check_interface $s
	fi
    done
}

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
}

case "$1" in
    start)
	if checkproc $DAEMON ; then
	    RETVAL=0
	else
	    echo -n "Starting iSCSI initiator service: "
	    iscsi_load_transport_modules
	    if grep -q bnx2i /proc/modules ; then
		startproc $BRCM_ISCSIUIO
	    fi
	    startproc $DAEMON $ARGS
	    RETVAL=$?
	    rc_status -v
	fi
	if [ "$RETVAL" == "0" ]; then
	    iscsi_login_all_nodes
	fi
	;;
    stop)
	iscsi_stop_sessions
	echo -n "Stopping iSCSI initiator service: "
	m=$(iscsiadm -m session 2> /dev/null)
	if [ -z "$m" ] ; then
	    killproc -KILL $DAEMON
	    RETVAL=$?
	    if grep -q bnx2i /proc/modules ; then
		killproc -KILL $BRCM_ISCSIUIO
	    fi
 	    RETVAL=$?
	else
	    RETVAL=1
	fi
	rc_failed $RETVAL
	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|reload)
	$0 stop
	RETVAL=$?
	if [ "$RETVAL" != "0" ]; then
		echo "Stopping iSCSI initiator service failed, not starting"
		exit $RETVAL
	fi
	sleep 1
	$0 start
	;;
    *)
	echo "Usage: $0 {start|stop|status|restart|reload}"
	exit 1
	;;
esac
rc_exit

