#! /bin/bash
# chkconfig: - 13 89
# description: iSCSI daemon

# Source function library.
[ -f /etc/init.d/functions ] || exit 0
. /etc/init.d/functions

[ -f /etc/sysconfig/iscsi ] && . /etc/sysconfig/iscsi

BASEDIR=/
PIDFILE=/var/run/iscsid.pid

# Increase the maximum TCP window size in Linux (well, socket memory, which is related) to this number
TCP_WINDOW_SIZE=1048576

# Timeouts are configurable in /etc/sysconfig/iscsi
#Timeouts to be used during iscsi startup
ESTABLISHTIMEOUT=${ESTABLISHTIMEOUT:-30}

#Timeouts to be used during iscsi shutdown
CONNFAILTIMEOUT=${CONNFAILTIMEOUT:-30}
ABORTTIMEOUT=${ABORTTIMEOUT:-10}
RESETTIMEOUT=${RESETTIMEOUT:-30}

# Flush iscsi dm device maps

# we put this here temporarily. In U5 or later we can move to a script
# that can be shared by iscsi and gnbd and handle shutdown RAID arrays
declare -a iscsi_mpaths

list_dms()
{
	i=0
	dev_sds=`iscsi-ls -l | grep Device | awk '{print $2}'| sed -e "s%/dev/%%"`
	dev_mpaths=`dmsetup table | grep multipath | awk '{print $1}' | sed -e "s/://"`

	echo "Searching for iscsi-based multipath maps"
	for dev_mpath in $dev_mpaths; do
		dmsetup_major_minor=`dmsetup deps $dev_mpath | cut -d')' -f1 | cut -d'(' -f2 | sed -e "s/, /:/"`
                for dev_sd in $dev_sds; do
	                # if sd device major/minor is in dmsetup deps output
                        sd_major_minor=`cat /sys/block/$dev_sd/dev`
                        if [ "$dmsetup_major_minor" = "$sd_major_minor" ]; then
                                iscsi_mpaths[$i]=$dev_mpath
                                        let "i += 1"
                                        break;
                        fi
                done
        done

	echo "Found" $i "maps"
}

deactivate_lvs()
{
	for ((j=0; j<$i; j++))
	do
		config=$(echo devices { filter = [ \""a|${iscsi_mpaths[$j]}|"\", \""r|.*|"\" ] })
		lvs=${lvs}$(lvs --noheadings --config "$config" -o vg_name,lv_name --separator "/" 2>/dev/null)
	done
	lvs=$(echo $lvs|tr [:space:] "\n"|sort|uniq)
	lvs=$(echo $lvs|tr "\n" " ")
	if [ -n "$lvs" ]; then
		echo "Deactivating LVs $lvs for multipath maps ${iscsi_mpaths[@]}" 
		lvchange -an $lvs
	fi
}

flush_iscsi_dms()
{
	for ((j=0; j<$i; j++))
	do
		echo "Flushing iscsi-based multipath map, ${iscsi_mpaths[$j]}" 
		multipath -f ${iscsi_mpaths[$j]}
	done
}

iscsi_network_boot()
{
	mtab=/etc/mtab
	iscsirootfs=$(awk '{ if ($1 !~ /^[ \t]*#/ && $2 == "/") { print $1; }}' $mtab)
	
	tmp="/dev/inbpdisk"
	
	dev=$(echo $tmp |sed -e "s/\//\\\\\//g")
	partnr=$(echo $iscsirootfs | sed -e "s/$dev//g")
	
	tmp=$tmp$partnr
	
	if [ $tmp != $iscsirootfs ] ; then
	    return 1
	else
	    return 0
	fi
}

shutdown_iscsi_hbas()
{
	for hba in /sys/class/scsi_host/* ; do
	    if [ -f $hba/proc_name ]; then
		name=`awk '{print $1}' < $hba/proc_name`
		if [ $name == "iscsi-sfnet" ]; then
		    echo > $hba/shutdown
		fi 
	    fi
	done
}

set_connfailtimeout()
{
	for hba in /sys/class/scsi_host/* ; do
	    if [ -f $hba/proc_name ]; then
		name=`awk '{print $1}' < $hba/proc_name`
		if [ $name == "iscsi-sfnet" ]; then
			if [ -f $hba/connfail_timeout ]; then
				connfailtimeout=`cat $hba/connfail_timeout`
				if [ $connfailtimeout -eq 0 ]; then
					echo "$CONNFAILTIMEOUT" > $hba/connfail_timeout
				fi
			fi
		fi 
	    fi
	done
}

set_aborttimeout()
{
	for hba in /sys/class/scsi_host/* ; do
	    if [ -f $hba/proc_name ]; then
		name=`awk '{print $1}' < $hba/proc_name`
		if [ $name == "iscsi-sfnet" ]; then
			if [ -f $hba/abort_timeout ]; then
				aborttimeout=`cat $hba/abort_timeout`
				if [ $aborttimeout -eq 0 ]; then
					echo "$ABORTTIMEOUT" > $hba/abort_timeout
				fi
			fi
		fi 
	    fi
	done
}
set_resettimeout()
{
	for hba in /sys/class/scsi_host/* ; do
	    if [ -f $hba/proc_name ]; then
		name=`awk '{print $1}' < $hba/proc_name`
		if [ $name == "iscsi-sfnet" ]; then
			if [ -f $hba/reset_timeout ]; then
				resettimeout=`cat $hba/reset_timeout`
				if [ $resettimeout -eq 0 ]; then
					echo "$RESETTIMEOUT" > $hba/reset_timeout
				fi
			fi
		fi 
	    fi
	done
}


start()
{
        # start
        update_boot_stage "Starting iscsi"

	if pidofproc iscsid > /dev/null 2>&1 ; then
	    echo -n "Starting iscsi: "
	    success "iscsid already running"
	    echo
	    exit 0
	fi


	echo -n "Checking iscsi config:  "

        # Do sanity checks before we start..
	if [ ! -e /etc/iscsi.conf ]; then
	    failure "Error: configuration file /etc/iscsi.conf is missing!"
	    echo
	    exit 1
	fi

	grep -Eq '^[^#]' /etc/iscsi.conf
	if [ $? -ne 0 ] ; then
	    failure "Error: Configuration file is empty, unable to start the driver"
            echo
	    exit 1
	fi

	if [ ! -f /etc/initiatorname.iscsi ] ; then
	    failure "Error: InitiatorName file /etc/initiatorname.iscsi is missing!"
	    echo
	    exit 1
	fi

	# see if we need to generate a unique iSCSI InitiatorName
	# this should only happen if the 
	if grep -q "^GenerateName=yes" /etc/initiatorname.iscsi ; then
	    if [ ! -x $BASEDIR/sbin/iscsi-iname ] ; then
		failure "Error: $BASEDIR/sbin/iscsi-iname does not exist, driver was not successfully installed"
                echo
		exit 1;
	    fi 
	    # Generate a unique InitiatorName and save it
	    INAME=`$BASEDIR/sbin/iscsi-iname`
	    if [ "$INAME" != "" ] ; then
		echo "## DO NOT EDIT OR REMOVE THIS FILE!" > /etc/initiatorname.iscsi
		echo "## If you remove this file, the iSCSI daemon will not start." >> /etc/initiatorname.iscsi
		echo "## If you change the InitiatorName, existing access control lists" >> /etc/initiatorname.iscsi
		echo "## may reject this initiator.  The InitiatorName must be unique">> /etc/initiatorname.iscsi
		echo "## for each iSCSI initiator.  Do NOT duplicate iSCSI InitiatorNames." >> /etc/initiatorname.iscsi
		printf "InitiatorName=$INAME\n"  >> /etc/initiatorname.iscsi
		chmod 600 /etc/initiatorname.iscsi
	    else
		failure "Error: failed to generate an iSCSI InitiatorName, driver cannot start."
		echo
		exit 1;
	    fi
        fi

	# make sure there is a valid InitiatorName for the driver
	if ! grep -q "^InitiatorName=[^ \t\n]" /etc/initiatorname.iscsi ; then
	    failure "Error: /etc/initiatorname.iscsi does not contain a valid InitiatorName."
	    echo
	    exit 1
	fi

        success "iscsi config check"
        echo

#FIXME: Should this be cycled
	# cycle the old log, since we can't guarantee anything is managing
	# it, and in any case it's now only used for debugging info.
	if [ -e /var/log/iscsi.log ] ; then
	    rm -f /var/log/iscsi.log.old
	    mv /var/log/iscsi.log /var/log/iscsi.log.old
	fi
	echo "" > /var/log/iscsi.log
	chmod 0600 /var/log/iscsi.log

	# Increase the maximum TCP window size to something that will give
	# reasonable performance for storage networking.  Use at least a 1 MB
	# max.
	# This only works if we have /proc, but the daemon assumes that anyway,
	# so there's no point trying to use sysctl.
# FIXME: Do we want to adjust this?
	if [ -e /proc/sys/net/core/rmem_max ] ; then
	    RMEM_MAX=`cat /proc/sys/net/core/rmem_max`
	    if [ $RMEM_MAX -lt $TCP_WINDOW_SIZE ] ; then
		echo "$TCP_WINDOW_SIZE" > /proc/sys/net/core/rmem_max
	    fi
	fi

	if [ -e /proc/sys/net/core/wmem_max ] ; then
	    WMEM_MAX=`cat /proc/sys/net/core/wmem_max`
	    if [ $WMEM_MAX -lt $TCP_WINDOW_SIZE ] ; then
		echo "$TCP_WINDOW_SIZE" > /proc/sys/net/core/wmem_max
	    fi
	fi

	if [ -e /proc/sys/net/ipv4/tcp_rmem ] ; then
	    max=`awk '{print $3}' /proc/sys/net/ipv4/tcp_rmem`
	    if [ $max -lt $TCP_WINDOW_SIZE ] ; then
		min=`awk '{print $1}' /proc/sys/net/ipv4/tcp_rmem`
		default=`awk '{print $2}' /proc/sys/net/ipv4/tcp_rmem`
		echo "$min $default $TCP_WINDOW_SIZE" > /proc/sys/net/ipv4/tcp_rmem
            fi
	fi

	if [ -e /proc/sys/net/ipv4/tcp_wmem ] ; then
	    max=`awk '{print $3}' /proc/sys/net/ipv4/tcp_wmem`
	    if [ $max -lt $TCP_WINDOW_SIZE ] ; then
		min=`awk '{print $1}' /proc/sys/net/ipv4/tcp_wmem`
		default=`awk '{print $2}' /proc/sys/net/ipv4/tcp_wmem`
		echo "$min $default $TCP_WINDOW_SIZE" > /proc/sys/net/ipv4/tcp_wmem
            fi
	fi

	if ! action "Loading iscsi driver: " modprobe iscsi_sfnet; then
            ret=$?
            exit $ret
        fi

	# By default, we try to load the scsi disk driver module.
	# If SCSI support is in modules, sd_mod won't get loaded
	# until after a /dev/sd* device is opened.  This means no 
	# messages about disks will be logged until a disk device 
	# is opened.  Worse, mounting by label won't work, since
	# it relies on /proc/partitions, which won't get updated
	# until the SCSI disk driver is loaded, creating a circular
	# dependency.  To work around these problems, we try to load 
	# the disk driver here.  If you're not using SCSI disks, 
	# you can comment this out.
	modprobe sd_mod > /dev/null 2>&1

	# Create /dev/iscsictl
	# wait for hotplug to create it (up to 5 seconds)
	i=0
	while [ ! -f /dev/iscsictl -a $i -lt 5 ]; do
	    sleep 1
	    i=$(( i + 1 ))
	done

	# hotplug failed us, just make the devnode
	if [ ! -f /dev/iscsictl ]; then
	    while read major device
	    do
	    if [ "$device" == "iscsictl" ]; then
	        mknod /dev/$device c $major 0
	    fi
	    done < /proc/devices
	fi

	echo -n "Starting iscsid: "
	daemon /sbin/iscsid
	echo

        sleep $ESTABLISHTIMEOUT

	# Make sure the K*iscsi scripts get called
	if [ -d /var/lock/subsys ] ; then 
	    touch /var/lock/subsys/iscsi 
	fi
        
 }


stop()
{
        ret=0

	list_dms
	deactivate_lvs
	flush_iscsi_dms

    	echo -n "Stopping iscsid: "
	if ! pidofproc iscsid > /dev/null 2>&1; then
		echo -n "iscsid not running"
	else
		killproc iscsid
	fi

        echo

	# We need a low connfail_timeout so that driver shutdown does not hang
	set_connfailtimeout;
	set_aborttimeout;
	set_resettimeout;

	# force shutdown
	shutdown_iscsi_hbas;
	if grep -q "iscsi_sfnet" /proc/modules ; then
	    sync
            echo -n "Removing iscsi driver: "
	    if rmmod iscsi_sfnet; then
                success "removing driver"
            else
                failure "removing driver"
                ret=1
            fi
	fi

	if [ -e /var/lock/subsys/iscsi ] ; then
 	    rm /var/lock/subsys/iscsi
	fi

        echo
        return $ret
}

case $1 in
 start)
        start
 ;;

 stop)
        stop
 ;;
 restart)
	# if iSCSI network boot then exit.
	#if iscsi_network_boot ; then
	#    echo "Since it is an iSCSI network boot therefore, driver cannot be stopped/restarted"
	#    exit 1
	#fi

	if stop; then
            start
        fi
	;;
 reload)
	# scan for new luns on existing targets
	iscsi-rescan
	# tell the daemon to re-read config file for any changes
        killproc iscsid -HUP
	;;
 restart-iscsid)
	if pidofproc iscsid > /dev/null 2>&1 ; then
		killproc iscsid
		daemon /sbin/iscsid
	fi
	;;
 status)
	status iscsid
	;;
 *)
	echo "Usage: /etc/init.d/iscsi { start | stop | restart | status | reload | restart-iscsid }"
        exit 1
	;;
esac


