#! /bin/bash
###############################################################################
# display sysfs information about zfcp devices (adapters, ports, units)
#
# Copyright IBM Corp. 2006.
###############################################################################

#ToDo:
# - combine scsi_device with class-tape-device

SCRIPTNAME="lszfcp"

SYSFS=`cat /proc/mounts | grep sysfs | awk '{ print $2 }'`
VERBOSITY=0
SHOW_ATTRIBUTES=0
SHOW_HOSTS=0
SHOW_PORTS=0
SHOW_DEVICES=0
unset PAR_BUSID PAR_WWPN PAR_LUN

OPTS=`getopt -o ahvHPDVTb:p:l: --long \
    attributes,help,version,hosts,ports,devices,verbose,busid:,wwpn:,lun: \
    -n \'$SCRIPTNAME\' -- "$@"`
eval set -- "$OPTS"

check_kernel_version()
{
    KERNEL=`uname -r | tr --complement "[:digit:]" "."`
    MAJOR=`echo $KERNEL | awk -F "." '{ print $1 }'`
    MINOR=`echo $KERNEL | awk -F "." '{ print $2 }'`
    SUB=`echo $KERNEL | awk -F "." '{ print $3 }'`

    if [ ${MAJOR:-0} -ne 2 -o ${MINOR:-0} -ne 6 -o ${SUB:-0} -lt 14 ]
    then
        echo "$SCRIPTNAME: Kernel versions prior to 2.6.14 are not supported."
	exit 1
    fi
}

printversion()
{
    cat <<EOF
$SCRIPTNAME: version %S390_TOOLS_VERSION%.
Copyright IBM Corp. 2006.
EOF
}

printhelp()
{
    cat <<EOF
Usage: $SCRIPTNAME [OPTIONS]

Provide information contained in sysfs about zfcp adapters, ports and
units that are online.


Options:

        -h, --help       print this help
        -v, --version    print version information
	-H, --hosts      show host information (default)
	-P, --ports      show remote port information
	-D, --devices    show SCSI device information
	-b, --busid      select specific busid
	-p, --wwpn       select specific port name
	-l, --lun        select specific LUN
	-a, --attributes show all attributes
 	-V, --verbose    show sysfs paths of associated class
                         and bus devices

Example: List for all zfcp adapters, ports and units the names
of their associated SCSI hosts, FC remote ports and SCSI devices.

    #> lszfcp -P -H -D
    0.0.3d0c host0
    0.0.3d0c/0x500507630300c562 rport-0:0-0
    0.0.3d0c/0x500507630300c562/0x4010403300000000 0:0:0:0

The default is to list bus_ids of all zfcp adapters and corresponding
SCSI host names (equals "lszfcp -H").
EOF
}

invalid_cmdline()
{
        echo "$SCRIPTNAME: $*"
	echo "$SCRIPTNAME: Try '$SCRIPTNAME --help' for more information."
	exit 1
}

check_cmdline()
{
    while true ; do
	case "$1" in
	    -a|--attributes) SHOW_ATTRIBUTES=1
			shift 1;;
	    -b|--busid) PAR_BUSID=$2
			shift 2;;
	    -h|--help) printhelp
			exit 0;;
	    -l|--lun) PAR_LUN=$2
			shift 2;;
	    -p|--wwpn) PAR_WWPN=$2
			shift 2;;
	    -v|--version) printversion
			exit 0;;
	    -H|--hosts)	SHOW_HOSTS=1
			shift 1;;
	    -D|--devices) SHOW_DEVICES=1
			shift 1;;
	    -P|--ports)	SHOW_PORTS=1
			shift 1;;
	    -V|--verbose) VERBOSITY=1
			shift 1;;
	    --) shift; break;;
	    *) echo "Internal error!" ; exit 1;;
	esac
    done
    if [ -n "$*" ]
    then
	invalid_cmdline invalid parameter $*
    fi
}

show_devices()
{
    DEVICE_LIST=`systool  -c scsi_device -p | \
	grep "Device path = .*$SYSFS/devices" \
	| awk '{ print $4 }'`

    for i in $DEVICE_LIST
    do
	SCSI_DEVICE_PATH=`eval echo $i`
	SCSI_DEVICE=`basename $SCSI_DEVICE_PATH`
	SCSI_TARGET_PATH=`dirname $SCSI_DEVICE_PATH`
	SCSI_TARGET=`basename $SCSI_TARGET_PATH`
	FC_RPORT_PATH=`dirname $SCSI_TARGET_PATH`
	FC_RPORT=`basename $FC_RPORT_PATH`
	SCSI_HOST_PATH=`dirname $FC_RPORT_PATH`
	SCSI_HOST=`basename $SCSI_HOST_PATH`
	ADAPTER_PATH=`dirname $SCSI_HOST_PATH`
	ADAPTER=`basename $ADAPTER_PATH`

	BLOCK_DEVICE=`basename "\`ls -l \$SCSI_DEVICE_PATH/block* 2>/dev/null\`"`
	GENERIC_DEVICE=`basename "\`ls -l \$SCSI_DEVICE_PATH/generic 2>/dev/null\`"`
	TMP=`systool -c block "$BLOCK_DEVICE" -p |
	    grep "Class Device path = .*$SYSFS/block" \
	    | awk '{ print $5 }'`
 	BLOCK_DEVICE_PATH=`eval echo $TMP`

	TMP=`systool -c scsi_generic "$GENERIC_DEVICE" -p |
	    grep "Class Device path = .*$SYSFS/class/scsi_generic" \
	    | awk '{ print $5 }'`
 	GENERIC_DEVICE_PATH=`eval echo $TMP`

	WWPN=`cat $SCSI_DEVICE_PATH/wwpn`
	LUN=`cat $SCSI_DEVICE_PATH/fcp_lun`

	if [ $LUN != ${PAR_LUN:-$LUN} ]
	then
	    continue
	fi
	if [ $WWPN != ${PAR_WWPN:-$WWPN} ]
	then
	    continue
	fi
	if [ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ]
	then
	    continue
	fi

	if [ $VERBOSITY = "0" ]
	then
	    echo "$ADAPTER/$WWPN/$LUN $SCSI_DEVICE"
	else
	    echo "$ADAPTER_PATH/$WWPN/$LUN"
	    echo "$SCSI_DEVICE_PATH"
	    echo "$BLOCK_DEVICE_PATH $GENERIC_DEVICE_PATH"
	fi

	if [ $SHOW_ATTRIBUTES = "1" ]
	then
	    systool -c scsi_device $SCSI_DEVICE -v | \
		grep -x -v ".*store method only.*\|.*$SCSI_DEVICE.*\|"
	    echo
	fi
    done
}

show_ports()
{
    RPORT_LIST=`systool -c fc_remote_ports -p | \
      grep "Device path = .*$SYSFS/devices" \
      | awk '{ print $4 }'`

    for i in $RPORT_LIST
    do
	FC_RPORT_PATH=`eval echo $i`
	FC_RPORT=`basename $FC_RPORT_PATH`
	SCSI_HOST_PATH=`dirname $FC_RPORT_PATH`
	SCSI_HOST=`basename $SCSI_HOST_PATH`
	ADAPTER_PATH=`dirname $SCSI_HOST_PATH`
	ADAPTER=`basename $ADAPTER_PATH`

	if [ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ]
	then
	    continue
	fi

        
	#FC_RPORT_PATH=`systool -c fc_remote_ports $FC_RPORT -v | \     ## workaround for libsysfs-2.0 bug
        FC_RPORT_PATH=`systool -c fc_remote_ports -v|grep $FC_RPORT| \
	  grep "Class Device path = .*$SYSFS/class" | awk '{ print $5 }'`
	FC_RPORT_PATH=`eval echo $FC_RPORT_PATH`
	WWPN=`cat $FC_RPORT_PATH/port_name`

	if [ $WWPN != ${PAR_WWPN:-$WWPN} ]
	then
	    continue
	fi

	if [ $VERBOSITY = "0" ]
	then
	    echo "$ADAPTER/$WWPN $FC_RPORT"
	else
	    echo "$ADAPTER_PATH/$WWPN"
	    echo "$FC_RPORT_PATH"
	fi

	if [ $SHOW_ATTRIBUTES = "1" ]
	then
	    systool -c fc_remote_ports $FC_RPORT -v | \
		grep -x -v ".*store method only.*\|.*$FC_RPORT.*\|"
	    echo
	fi
    done
}

show_hosts()
{
    HOST_LIST=`systool -c fc_host -p | \
     grep "Device path = .*$SYSFS/devices" \
    | awk '{ print $4 }'`

    for i in $HOST_LIST
    do
	SCSI_HOST_PATH=`eval echo $i`
	SCSI_HOST=`basename $SCSI_HOST_PATH`
	ADAPTER_PATH=`dirname $SCSI_HOST_PATH`
	ADAPTER=`basename $ADAPTER_PATH`

	if [ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ]
	then
	    continue
	fi

	SCSI_HOST_PATH=`systool -c scsi_host $SCSI_HOST -v | \
	    grep "Class Device path = .*$SYSFS/class" | awk '{ print $5 }'`
	SCSI_HOST_PATH=`eval echo $SCSI_HOST_PATH`

	FC_HOST_PATH=`systool -c fc_host $SCSI_HOST -v | \
	    grep "Class Device path = .*$SYSFS/class" | awk '{ print $5 }'`
	FC_HOST_PATH=`eval echo $FC_HOST_PATH`

	if [ $VERBOSITY = "0" ]
	then
	    echo "$ADAPTER $SCSI_HOST"
	else
	    echo "$ADAPTER_PATH"
	    echo "$FC_HOST_PATH"
	    echo "$SCSI_HOST_PATH"
	fi

	if [ $SHOW_ATTRIBUTES = "1" ]
	then
	    systool -b ccw $ADAPTER -v | \
		grep -x -v ".*store method only.*\|.*$ADAPTER.*\|"
	    systool -c fc_host $SCSI_HOST -v | \
		grep -x -v ".*store method only.*\|.*$SCSI_HOST.*\|"
	    systool -c scsi_host $SCSI_HOST -v | \
		grep -x -v ".*store method only.*\|.*$SCSI_HOST.*\|"
	    echo
	fi
    done
}

check_zfcp_support()
{
	if [ -e $SYSFS/bus/ccw/drivers/zfcp ]
	then
		return 0
	else
		echo -e "\nNo zfcp support available!"
		echo "Please load the zfcp module"
		echo "or compile the kernel with zfcp support."
		echo ""
		exit 1
	fi
}

check_sysfs()
{
	if [ -z $SYSFS ]
        then
        	echo "sysfs not available"
                exit 1
        fi
}

check_cmdline $*
check_kernel_version
check_sysfs
check_zfcp_support
default=1
if [ $SHOW_HOSTS = "1" ]
then
    default=0; show_hosts
elif [ $SHOW_PORTS = "0" -a $SHOW_DEVICES = "0" -a -n "$PAR_BUSID" ]
then
    default=0; show_hosts
fi
if [ $SHOW_PORTS = "1" ]
then
    default=0; show_ports
elif [ $SHOW_HOSTS = "0" -a $SHOW_DEVICES = "0" -a -n "$PAR_WWPN" ]
then
    default=0; show_ports
fi
if [ $SHOW_DEVICES = "1" ]
then
    default=0; show_devices
elif [ $SHOW_HOSTS = "0" -a $SHOW_PORTS = "0" -a -n "$PAR_LUN" ]
then
    default=0; show_devices
fi
if [ $default == "1" ]
then
    show_hosts
fi
