#!/bin/bash
#
# Name: lssg
# Copyright: (c)Copyright 2004-2008 Hewlett-Packard Development Company, L.P.
#
# Description: lists all sg devies and a description
#              of each device
#
# Modification History
#
# Chad Dupuis   08/26/04 Initial Development
# Chad Dupuis   08/23/05 Added command line options to determine whether to print
#                        the world wide name and UUID of each device; Use
#                        /sys/block to get a listing of sd devices
#                        on the system
# Chad Dupuis	11/16/06 Fixed infinite loop if the vendor or product id for a
#                        particular sg device is blank; Add check to make sure
#                        script can't be run on 2.4 kernels
# Chad Dupuis	03/20/07 Do not list sg device if WWN is all 0's
# Chad Dupuis   06/19/07 With 2.6 kernels, do not stop when scsi_info doesn't work
# Chad Dupuis	06/22/07 Fix hang when scanning
# Chad Dupuis	07/26/07 Check for /sys/class/scsi_generic directory as the
#                        directory will not exist if the sg driver is not loaded
# Chad Dupuis	07/31/07 Add -c option to allow device data to be printed from
#                        a cache instead of scanning
# Chad Dupuis   08/11/08 Change scsi_info.26 to scsi_info

#
# Functions
#

# This function prints a help message

print_help () {
 echo "Usage: $0 [-w|l|c|h]"
 echo ""
 echo "Prints all bound /dev/sd* devices"
 echo ""
 echo "-w: print world wide node name of devices"
 echo "-l: print inquiry page 0x83 UUID of devices"
 echo "-c: print cached data instead of scanning"
 echo "-h: print help message"
 exit 0
}

# gets a list of devices

device_list () {
	DEVICELIST="`ls $SGDIR | sed 's/sg//g' | sort -g`"
}
 	
space_str () {
 STRINGSIZE=$1
 STR=$2 
 SPACESTR=""
 
 SIZE=`echo $STR | wc -c`
 NUM_OF_SPACES=`expr $STRINGSIZE - $SIZE`

 COUNT=$NUM_OF_SPACES

 while [ $COUNT -ne 0 ]
 do
  SPACESTR=$SPACESTR" "
  COUNT=`expr $COUNT - 1`
 done
}

# Name: print_device_data
# Description: Print the data for the current device
# In: None
# Out: None
# Returns: None

print_device_data () {
	echo -n "${DEVICE}${DEVICESPACE} ${CONN}${CONNSPACE} $VENDOR $MODEL $FWREV"
	
	if [ $WWIDFLAG -eq 1 ]
	then
	 	echo -n "  $WWID"
	fi
	
	if [ $LUNIDFLAG -eq 1 ]
 	then
 		echo -n "  $LUNID"
 	fi
 	echo ""
}

# Name: format_device_data
# Description: Formats the various variables that are used to print the device
#              data
# In: None
# Out: None
# Returns: None

format_device_data () {
	CONN=`echo $OUTPUT | awk 'BEGIN {FS=":"} {print $1}' | awk 'BEGIN {FS="="} {print $2}' | sed 's/"//g'`
 	VENDOR=`echo $OUTPUT | awk 'BEGIN {FS=":"} {print $2}' | awk 'BEGIN {FS="="} {print $2}' | sed 's/"//g'`
 	MODEL=`echo $OUTPUT | awk 'BEGIN {FS=":"} {print $3}' | awk 'BEGIN {FS="="} {print $2}' | sed 's/"//g'`
 	FWREV=`echo $OUTPUT | awk 'BEGIN {FS=":"} {print $4}' | awk 'BEGIN {FS="="} {print $2}' | sed 's/"//g'`
 	WWID=`echo $OUTPUT | awk 'BEGIN {FS=":"} {print $5}' | awk 'BEGIN {FS="="} {print $2}' | sed 's/"//g'`
 	LUNID=`echo $OUTPUT | awk 'BEGIN {FS=":"} {print $6}' | awk 'BEGIN {FS="="} {print $2}' | sed 's/"//g'`
	
	# get the number of spaces to pad after the connection address

 	space_str 6 "${DEVICE}"
 	DEVICESPACE=$SPACESTR 
 	space_str 16 $CONN
 	CONNSPACE=$SPACESTR
 
 	# format world wide ID string

 	if [ "$WWID" != "" ]
 	then
  		WWID="`echo $WWID | cut -c 1-4`"-"`echo $WWID | cut -c 5-8`"-"`echo $WWID | cut -c 9-12`"-"`echo $WWID | cut -c 13-16`"
	fi 
}

# Name: print_scan_data
# Description: Actually send INQUIRY commands to the devices to get the device
#              data
# In: None
# Out: None
# Returns: None

print_scan_data () {
	# If we've made it this far, delete easier cached data
	rm -f $CACHEFILE
	
	# get list of device suffixes
	device_list

	# loop through devices and query the devices directly
	# to obtain information 

	for i in $DEVICELIST
	do
 		DEVICE="sg${i}"

		# check to see if device is bound 
 		$SCSI_INFO /dev/$DEVICE 1>/dev/null 2>/dev/null

 		if [ $? -ne 0 ]
 		then
			# The behavior when scsi_info fails is slightly different depending on the 
			# kernel version.  With 2.4 kernels we want to exit because we can assume
			# consecutive device lettering.  With 2.6, we have an exact listing of all
			# devices from the /sys/block directory so simply skip to the next device

			if [ "$KERNELVERSION" = "2.4" ]
			then	
				exit 0
			else
				continue
			fi
 		fi

 		# get device info from scsi_info
 		OUTPUT=`$SCSI_INFO /dev/$DEVICE` 
		
		# Format the device data
		format_device_data

		# If the vendor data is blank then this is a pseudo
		# MultiPulse device; don't print it

		if [ "$VENDOR" = "" ]
		then
			continue
		fi
		
	        # Save device information in cache file
                echo "DEVICE=$DEVICE:$OUTPUT" >> $CACHEFILE

		# Actually print out the data for this device
		print_device_data
	done
}

# Name: print_cache_data
# Description: Prints sd device data from the last scan instead
#              of doing a fresh scan
# In: None
# Out: None
# Returns: None
print_cache_data () {
	if test ! -f $CACHEFILE
	then
		return
	fi

	while read line
	do
		DEVICE="`echo $line | awk 'BEGIN {FS=":"} {print $1}' | awk 'BEGIN {FS="="} {print $2}'`"
		OUTPUT="`echo $line | sed 's/DEVICE=[a-z]*[0-9]*://'`"
		
		# Format the device data
                format_device_data

		# If the vendor data is blank then this is a pseudo
                # MultiPulse device; don't print it

                if [ "$VENDOR" = "" ]
                then
                        continue
                fi

                # Actually print out the data for this device
                print_device_data
	done < $CACHEFILE

}
 
# This function parses parameters passed in from the command line

read_args () {

 WWIDFLAG=0
 LUNIDFLAG=0
 HELPFLAG=0
 CACHEFLAG=0

 if [ $# -gt 0 ]
 then
        # parse command line into arguments

        getopt wlch $* 1>/dev/null 2>/dev/null

        # check result of parsing

        if [ $? != 0 ]
        then
                echo "Bad argument or missing argument"
                exit 1
        fi

        set -- `getopt wlch $*`

        while [ $1 != -- ]
        do
                case $1 in
                        -w) WWIDFLAG=1;;
                        -h) HELPFLAG=1;;
			-l) LUNIDFLAG=1;;
			-c) CACHEFLAG=1;;
                        *) echo "$1 is an illegal argument"
                           exit 1;;
                esac
                shift   # next flag
        done

        shift   # skip --

        if [ $HELPFLAG -eq 1 ]
        then
                print_help
                exit 0
        fi

	if [ $CACHEFLAG -eq 1 ] && test -f $CACHEFILE
	then
		print_cache_data
		exit 0
	fi
 fi
}

# 
# defines
#

SCSI_INFO="/opt/hp/hp_fibreutils/scsi_info"
SGDIR="/sys/class/scsi_generic"
CACHEFILE="/opt/hp/hp_fibreutils/lssg.cache"

#
# Script Main
#

# Check to make sure we are minimally a 2.6 kernel
#
# Feb 14 2012, Rahul / Keith, removed this check, no longer needed.
# if [ `uname -r | awk 'BEGIN {FS="."} {print $3}' | awk 'BEGIN {FS="-"} {print $1}'` -lt 16 ]
# then
#       echo ""
#       echo "This RPM is not supported on kernels less than 2.6.16"
#       echo ""
#       exit 1
# fi

# Check to see if we're greater than RHEL 5.2
MAJVER=0
MINVER=0

if test -f /etc/redhat-release
then
        #
        # RHEL case
        if [ `grep -c "Red Hat Enterprise Linux" /etc/redhat-release` -gt 0 ]
        then
                VERSTR="`grep "Red Hat Enterprise Linux" /etc/redhat-release | awk '{print $7}'`"

                if [ "$VERSTR" != "" ]
                then
                        MAJVER="`echo $VERSTR | awk 'BEGIN {FS="."} {print $1}'`"
                        MINVER="`echo $VERSTR | awk 'BEGIN {FS="."} {print $2}'`"
                fi
        fi

        #
        # OEL case
        if [ `grep -c "Enterprise Linux Enterprise Linux" /etc/redhat-release` -gt 0 ]
        then
                VERSTR="`grep "Enterprise Linux Enterprise Linux" /etc/redhat-release | awk '{print $7}'`"

                if [ "$VERSTR" != "" ]
                then
                        MAJVER="`echo $VERSTR | awk 'BEGIN {FS="."} {print $1}'`"
                        MINVER="`echo $VERSTR | awk 'BEGIN {FS="."} {print $2}'`"
                fi
        fi

        # Now check to see if we're an update of RHEL 5
        if [ "$MAJVER" = "5" ]
        then
                if [ $MINVER -lt 3 ]
                then
                        echo ""
                        echo "This RPM is not supported on updates less than RHEL or OEL 5.3"
                        echo ""
                        exit 1
                fi
        fi
fi

# Check to see if we're greater than SLES 10 SP 2

if test -f /etc/SuSE-release
then
        SLESREV="`grep VERSION /etc/SuSE-release | awk '{print $3}'`"
        UPDATE="`grep PATCHLEVEL /etc/SuSE-release | awk '{print $3}'`"

        if [ "$SLESREV" = "10" ]
        then
                if [ $UPDATE -lt 3 ]
                then
                        echo ""
                        echo "This RPM is not supported on update less than SLES 10 SP 3"
                        echo ""
                        exit 1
                fi
        fi
fi

# Check to make sure that scsi_info exists

ls $SCSI_INFO 1>/dev/null 2>/dev/null

if [ $? -ne 0 ]
then
 echo "$SCSI_INFO does not exist"
 exit 1
fi

# Check to make sure that the sg driver is loaded
if test ! -d $SGDIR
then
	echo "Could not find $SGDIR."
	echo "It is possible that you do not have the sg.ko module loaded."
	exit 1
fi

# parse argument list

read_args $*

if [ $CACHEFLAG -eq 1 ] && test -f $CAHCEFILE
then
	print_cache_data
else
	print_scan_data	
fi

