#!/bin/bash
#
# Name: probe-luns
# Copyright: (c)2002-2005 Hewlett-Packard Company
#
# Description: forces a rescan at certain connection addresses
#              by the scsi mid-layer
#
# CD		04/21/04 Search for targets by keying off of the
#                        "target" string only
# CD		05/17/04 put a delay in between each add/remove scsi
#                        command so as not to panic the kenrel
# CD 		05/19/04 changed sleep to usleep so that the delay
#                        works on both Red Hat and SUSE
# CD		06/11/04 Changes for SLES 9 (2.6 kernel)
# CD		02/01/05 Added command line options to just do
#                        remove-single-device and add-single-device
# CD		02/04/05 Fixed bug where garbage string was being sent
#                        to /proc/scsi/scsi during scan; Don't scan
#                        root device
# CD		03/23/05 Do not execute "scsi remove-single-device" if
#                        we are running on RHEL 4 (messes up LVM)
# CD		05/04/05 Do not execute "scsi remove-single-device" for
#                        any 2.6 distribution.  Executing it deallocates
#                        the memory for the device thereby possibly causing
#                        an application to try to read/write to a non-
#                        existent device
# CD		05/11/05 Fix grep typos
 
# defines

LUNSTART=0
LUNEND=32
LSSD=/opt/hp/hp_fibreutils/lssd

# detect if we are using a 2.6 kernel or not

if [ "`uname -r | cut -c 1-3`" = "2.6" ]
then
 RHEL4=y
else
 RHEL4=n
fi

# This is the delay in microseconds between each echo "scsi..." command

PROBEDELAY=100000

#
# functions
#

# produces a list of adapter instances
# outputs variable ADAPTERS

adapter_inst () {
 ADAPTERS=`find /proc/scsi | grep /[0-9/].* | grep qla | sort -u`
}


# determine root devices
# output variabel ROOTINST

determine_root_inst () {
 
 # determine root device from mounted filesystems
 ROOTDEV=`mount | grep "on / " | awk '{print $1}' | sed 's/\/dev\///g' | sed 's/[0-9/]//g'`

 # determine root device scsi instance (if there is one) using lssd
 if [ "`echo $ROOTDEV | grep sd`" != "" ]
 then
 	ROOTINST=`$LSSD | grep $ROOTDEV | awk '{print $2}' | sed 's/,/ /g'`
 else
 	ROOTINST=""
 fi
}


# forces a rescan of the SCSI midlayer for all qla* adapters

scan_all () {

 # get root device scsi address (if one exists)

 determine_root_inst

 # get a list of qla* adapters in the system

 adapter_inst

 # perform midlayer removal

 # do not perform "scsi remove-single-device" if we are RHEL 4
 if [ "$RHEL4" != "y" ]
 then
	for i in $ADAPTERS
 	do
		# get list of targets

		TARGETS=`cat ${i} | grep target | grep -v adapter | awk 'BEGIN {FS="="} {print $1}' | awk 'BEGIN {FS="-"} {print $4}'`
		INST=`echo $i | awk 'BEGIN {FS="/"} {print $5}'`

		for j in $TARGETS
		do
			CNT=$LUNSTART

			# send scsi remove command to midlayer
			while [ $CNT -le $LUNEND ]
			do
				# do not perform scsi command on root device
				if [ "$ROOTINST" != "$INST 0 $j $CNT" ]
				then
					printf "\rRemoving $i, target $j, LUN $CNT   "
					echo "scsi remove-single-device $INST 0 $j $CNT" > /proc/scsi/scsi
					usleep $PROBEDELAY
				fi
				CNT=`expr $CNT + 1`
			done
		done
 	done
 fi
 echo ""

 # perform midlayer scanning

 for i in $ADAPTERS
 do

	# get list of targets

        TARGETS=`cat ${i} | grep target | grep -v adapter | awk 'BEGIN {FS="="} {print $1}' | awk 'BEGIN {FS="-"}
{print $4}'`
	INST=`echo $i | awk 'BEGIN {FS="/"} {print $5}'`

	for j in $TARGETS
	do
		CNT=$LUNSTART

		# send scsi add command to midlayer

		while [ $CNT -le $LUNEND ]
		do
			# do not perform scsi command on root device!
                        if [ "$ROOTINST" != "$INST 0 $j $CNT" ]
			then	
				printf "\rScanning $i, target $j, LUN $CNT   "
				echo "scsi add-single-device $INST 0 $j $CNT" > /proc/scsi/scsi
				usleep $PROBEDELAY
			fi
			CNT=`expr $CNT + 1`
		done
	done
 done

 echo ""
 print_scsi
}

# scan a particular adapter instance

scan_instance () {
 INST=$1
 
 # determine root device scsi address (if one exists)
 
 determine_root_inst
 
 # verify that instance exists

 DIRS="/proc/scsi/qla2200 /proc/scsi/qla2300 /proc/scsi/qla2xxx"
 FOUND=n

 for i in $DIRS
 do
	find ${i}/${INST} 1>/dev/null 2>/dev/null

  	if [ $? -eq 0 ]
  	then
   		FOUND=y
   		ADAPTER="${i}/${INST}"
  	fi
 done

 if [ "$FOUND" = "n" ]
 then
	echo "There is no SCSI adapter instance $INST"
	exit 1
 fi

 # get list of targets

 TARGETS=`cat ${ADAPTER} | grep target | grep -v adapter | awk 'BEGIN {FS="="} {print $1}' | awk 'BEGIN {FS="-"} {print $4}'`

 # remove LUNs in midlayer for this adapter instance

 # do not perform "scsi remove-single-device" if we are RHEL 4
 if [ "$RHEL4" != "y" ]
 then
 	for j in $TARGETS
 	do
		CNT=$LUNSTART
	
		# send scsi remove command to midlayer
		while [ $CNT -le $LUNEND ]
		do
			# do not perform scsi command on root device! and do not exeucte
			# "scsi remove-single-device if we are running RHEL 4
 	               if [ "$ROOTINST" != "$INST 0 $j $CNT" ] || [ "$RHEL4" = "y" ]
			then
				printf "\rRemoving ${i}/${INST}, target $j, LUN $CNT   "
				echo "scsi remove-single-device $INST 0 $j $CNT" > /proc/scsi/scsi
				usleep $PROBEDELAY
			fi
			CNT=`expr $CNT + 1`
		done
 	done
 fi
 echo ""

 # force midlayer to rescan this adapter instance

 for j in $TARGETS
 do
	CNT=$LUNSTART

	# send scsi add command to midlayer

	while [ $CNT -le $LUNEND ]
	do
		# do not perform scsi command on root device!
                if [ "$ROOTINST" != "$INST 0 $j $CNT" ]
		then
			printf "\rScanning ${i}/${INST}, target $j, LUN $CNT   "
			echo "scsi add-single-device $INST 0 $j $CNT" > /proc/scsi/scsi
			usleep $PROBEDELAY
		fi
		CNT=`expr $CNT + 1`
	done
 done

 echo ""
 print_scsi ${INST}
}

# print out device information from /proc/scsi/scsi

print_scsi () {

 # if there is no arguments passed in, get all instance numbers
 # on the system

 if [ "$1" = "" ]
 then
	hbas=`find /proc/scsi | grep /[0-9/].* | grep qla | sort -u | awk 'BEGIN {FS="/"} {print $5}'`
 else
 	hbas=$1
 fi

 # print data from /proc/scsi/scsi

 printf "\r                                                      \n\n"
 for inst in ${hbas}
 do
    cat /proc/scsi/scsi | tr -d '\n'|
    awk '{ for(i=1;i<=NF;i++) if(index($i,"scsi")!=0)printf("\n%s ",$i); else printf("%s ",$i)}' | \
    awk 'BEGIN{n=split("scsi|Channel|Id|Lun|Vendor|Model|Rev|Type",ar,"|");} \
         {for(i=1;i<NF;i++)for(j=1;j<=n;j++) \
          if(index($i,ar[j])){printf(" %s",index($i,"scsi")?$i:$(i+1));break}print "";}' | \
    awk '{if(index($0,"scsi'${inst}'")) \
          printf("%-6.6s %2.2s %2.2s %2.2s %-10.10s %-10.10s %-10.10s %s\n", $1,$2,$3,$4,$5,$6,$7,index($8,"known")?"":$8);}'
  done
  echo
}


# do just a scsi remove-single-device on all QLogic adapter devices

remove_all () {
 
 # do not perform "scsi remove-single-device" if we are RHEL 4
 if [ "$RHEL4" != "y" ]
 then
 	# get a list of qla* adapters in the system

 	adapter_inst

 	# get scsi address of root device (if one exists)
 
 	determine_root_inst
 
 	# perform midlayer removal

 	for i in $ADAPTERS
 	do
		# get list of targets

		TARGETS=`cat ${i} | grep target | grep -v adapter | awk 'BEGIN {FS="="} {print $1}' | awk 'BEGIN {FS="-"} {print $4}'`
		INST=`echo $i | awk 'BEGIN {FS="/"} {print $5}'`

		for j in $TARGETS
		do
			CNT=$LUNSTART

			# send scsi remove command to midlayer
			while [ $CNT -le $LUNEND ]
			do
				# do not perform scsi command on root device 
	                        if [ "$ROOTINST" != "$INST 0 $j $CNT" ] || [ "$RHEL4" = "y" ]
				then
					printf "\rRemoving $i, target $j, LUN $CNT   "
					echo "scsi remove-single-device $INST 0 $j $CNT" > /proc/scsi/scsi
					usleep $PROBEDELAY
				fi
				CNT=`expr $CNT + 1`
			done
		done
 	done
 	echo ""
 fi
}


# do just a scsi add-single-device on all QLogic adapter devices

add_all () {
 # get a list of qla* adapters in the system

 adapter_inst

 # get scsi address of root device (if one exists)
 
 determine_root_inst
 
 # do scsi add-single-device on all QLogic devices
 for i in $ADAPTERS
 do

	# get list of targets

        TARGETS=`cat ${i} | grep target | grep -v adapter | awk 'BEGIN {FS="="} {print $1}' | awk 'BEGIN {FS="-"}
{print $4}'`
	INST=`echo $i | awk 'BEGIN {FS="/"} {print $5}'`

	for j in $TARGETS
	do
		CNT=$LUNSTART

		# send scsi add command to midlayer

		while [ $CNT -le $LUNEND ]
		do
			# do not perform scsi command on root device!
                        if [ "$ROOTINST" != "$INST 0 $j $CNT" ]
			then
				printf "\rScanning $i, target $j, LUN $CNT   "
				echo "scsi add-single-device $INST 0 $j $CNT" > /proc/scsi/scsi
				usleep $PROBEDELAY
			fi
			CNT=`expr $CNT + 1`
		done
	done
 done
 echo ""
}

#
# script main
#

# parse argument 1

if [ $# -eq 0 ]
then
 echo "Usage: probe-luns -a|-i|-r|-d instance"
 echo ""
 echo "-a: force SCSI midlayer to rescan all qla2xxx devices"
 echo "-i: force SCSI midlayer to rescan a specific instance"
 echo "-r: force SCSI midlayer to remove all qla2xxx devices"
 echo "-d: force SCSI midlayer to add all qla2xxx devices"
 exit 0
elif [ "$1" = "-a" ]
then
 scan_all
elif [ "$1" = "-r" ]
then
 remove_all
elif [ "$1" = "-d" ]
then
 add_all
elif [ "$1" = "-i" ]
then
 if [ $# -ne 2 ]
 then
 	echo "-i requires an argument"
 	exit 1
 fi

 scan_instance $2
else
 echo "$1: Invalid argument"
 exit 1
fi

exit 0
