#!/bin/bash
#
# lsdasd - Tool to list information about DASDs
#
# Copyright IBM Corp. 2003, 2017
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
#

CMD=$(basename $0)
SYSFSDIR="/sys"

#------------------------------------------------------------------------------
# Print usage
#------------------------------------------------------------------------------
function PrintUsage() {
	cat <<-EOD
		Usage: $(basename $0) <options> [<device>]

		<options> ::=
		 	-a|--offline
		 		Include devices that are currently offline.
		 	-b|--base
		 		Include only base devices.
		 	-h|--help
		 		Print this text and exit.
		 	-s|--short
		 		Strip leading 0.0. from bus IDs.
		 	-u|--uid
		 		Print and sort by uid.
		 	-c|--compat
		 		Print old version of lsdasd output.
		 	-l|--long
		 		Print extended information about DASDs.
		 	-H|--host-access-list
		 		Print information about hosts accessing DASDs.
		 	-P|--copy-pairs
		 		Print information about copy pairs.
		 	-v|--verbose
		 		For compatibility/future use. Currently ignored.
		 	--version
		 		Show tools and command version.

		<device> ::= <bus ID>
		 	Limit output to one or more devices which are given as a bus ID.
	EOD
}

function PrintVersion()
{
	cat <<-EOD
	$CMD: version 2.31.0-build-20250827
	Copyright IBM Corp. 2003, 2017
	EOD
}

#------------------------------------------------------------------------------
# Helper function to check a device string.
#------------------------------------------------------------------------------
function CheckDeviceString() {
	local X

	X=$(
		echo "$1" |
		awk --posix -F. '
			function PrintBusID(css, grp, devno) {
				while(length(devno) < 4)
					devno = "0" devno
				print css "\\." grp "\\." devno "$"
			}
			NF == 1 && $1 ~ /^[0-9a-fA-F]{1,4}$/ {
				PrintBusID("0","0", $1)
				next
			}
			NF != 3 || $1 !~ /^[0-9a-fA-F]{1,2}$/ {
				next
			}
			$2 !~ /^[0-9a-fA-F]{1,2}$/ {
				next
			}
			$3 !~ /^[0-9a-fA-F]{1,4}$/ {
				next
			}
			{
				PrintBusID($1, $2, $3)
			}
		'
		)

	if [ "$X" != "" ]; then
		echo $X
		return 0
	fi

	return 1
}

#------------------------------------------------------------------------------
# Generate list of DASDs
#------------------------------------------------------------------------------
function listDASDDeviceDirectories() {
    DRIVERECKD="$SYSFSDIR/bus/ccw/drivers/dasd-eckd/"
    DRIVERFBA="$SYSFSDIR/bus/ccw/drivers/dasd-fba/"
    SEARCHDIRS=

    if [[ -d "$DRIVERECKD" ]]; then
	SEARCHDIRS="$DRIVERECKD"
    fi
    if [[ -d "$DRIVERFBA" ]]; then
	SEARCHDIRS="$SEARCHDIRS $DRIVERFBA"
    fi
    if [[ -n "$SEARCHDIRS" ]]; then
        find $SEARCHDIRS -type l -printf "%h/%l\n" 2> /dev/null
    else
        # The above paths may become invalid in the future, so we keep the
        # following query as backup:
        find "$SYSFSDIR/devices" -type l -name "driver" -lname "*/dasd*" \
		    -printf "%h\n" 2> /dev/null
    fi
    return 0
}

#------------------------------------------------------------------------------
# find dasd directory in debugfs
#------------------------------------------------------------------------------
function findDASDDebugfsDirectorie() {
    local mntentries

    while read -a mntentries
    do
	if [[ "${mntentries[2]}" == "debugfs" ]]
	then
	    DASD_DBF_DIR="${mntentries[1]}"
	    break;
	fi
    done < /etc/mtab
    if [[ "$DASD_DBF_DIR" == "" ]]
    then
	echo "$CMD: No debugfs mount point found" >&2
	exit 1
    fi
    DASD_DBF_DIR="$DASD_DBF_DIR/dasd"
    if [[ ! -d "$DASD_DBF_DIR" ]]
    then
	echo "$CMD: Default DASD debugfs directory $DASD_DBF_DIR does not exist" >&2
	exit 1
    fi
}

#------------------------------------------------------------------------------
# The blockpath can usually be found over the DEVPATH
# In case of a swapped Copy Pair device the blockdev is likely associated
# with one of the secondary devices
# this function checks if the blockdev is associated with the primary device
# and if not search for it in the secondary device list
#------------------------------------------------------------------------------
function setCorrectBlockPath() {
	local DRIVERECKD="$SYSFSDIR/bus/ccw/drivers/dasd-eckd/"
	local DRIVERFBA="$SYSFSDIR/bus/ccw/drivers/dasd-fba/"
	local SEARCHDIRS=
	local SEARCHLIST=

	if [[ "$COPYROLE" == "none" ]] || [[ -d "$DEVPATH/block" ]]; then
		BLOCKPATH="$DEVPATH"/block/dasd*
		return
	fi

	if [[ -d "$DRIVERECKD" ]]; then
		SEARCHDIRS="$DRIVERECKD"
	fi
	if [[ -d "$DRIVERFBA" ]]; then
		SEARCHDIRS="$SEARCHDIRS $DRIVERFBA"
	fi

	SEARCHLIST=$(echo $SECONDARY_LIST | sed 's/,/ /g')
	for SEARCHDEV in $SEARCHLIST
	do
		if [[ -n "$SEARCHDIRS" ]]; then
			PRIM_DEVPATH=$(find $SEARCHDIRS -type l -name $SEARCHDEV \
					    -printf "%h/%l\n" 2> /dev/null)
			if [[ -d "$PRIM_DEVPATH/block" ]]; then
				break
			fi
		else
			# The above paths may become invalid in the future, so we keep the
			# following query as backup:
			PRIM_DEVPATH=$(find "$SYSFSDIR/devices" -type l -name $SEARCHDEV \
					    "driver" -lname "*/dasd*" -printf "%h\n" \
					    2> /dev/null)
		fi
	done

	BLOCKPATH="$PRIM_DEVPATH"/block/dasd*
}

#------------------------------------------------------------------------------
# gather Copy Pair related data
#------------------------------------------------------------------------------
function gatherCopyPairData() {
	read COPYPAIR 2> /dev/null < $DEVPATH/copy_pair
	PRIMARY_UID=`echo $COPYPAIR | cut -d " " -f1 | cut -d "," -f1`
	COPYPAIR=`echo "$COPYPAIR" | sed 's/ /,/g' | sed 's/^,//' `
	SECONDARY_LIST=`echo ${COPYPAIR//$PRIMARY_UID","/} `
}

#------------------------------------------------------------------------------
# gather device data and call appropriate output function
#------------------------------------------------------------------------------
function gatherDeviceData() {
	while read DEVPATH
	do
		#-------------------------------------------#
		# gather information from device attributes #
		#-------------------------------------------#
		read ONLINE 2> /dev/null < $DEVPATH/online || continue
		if [[ "$ONLINE" == 0 ]] &&
			[[ "$PRINTOFFLINE" == "false" ]]; then
			continue
		fi
		read ALIAS 2> /dev/null < $DEVPATH/alias || continue
		read DEV_UID 2> /dev/null < $DEVPATH/uid || continue
		read READONLY 2> /dev/null < $DEVPATH/readonly || continue
		read DISCIPLINE 2> /dev/null < $DEVPATH/discipline || continue
		read ESE 2> /dev/null < $DEVPATH/ese
		read COPYROLE 2> /dev/null < $DEVPATH/copy_role

		if [[ "$COPYROLE" != "none" ]]; then
			gatherCopyPairData
		fi
		# Block device specific information is only available for
		# devices that are online and not a PAV alias
		if [[ ! "$ONLINE" == 0 ]] && [[ ! "$ALIAS" == 1 ]] &&
			   [[ ! "$COPYROLE" == "secondary" ]]; then
		        #find device Path to the block device
		        setCorrectBlockPath
		    	set - $BLOCKPATH
			MAJMIN=
			MAJOR=
			MINOR=
			SIZE=
			SSIZE=
			if [[ -d "$1" ]]; then
				cd -P "$1"
				BLOCKPATH=$PWD
				BLOCKNAME=${BLOCKPATH##*/}
				read MAJMIN 2> /dev/null < $BLOCKPATH/dev || continue
				MAJOR=${MAJMIN%%:*}
				MINOR=${MAJMIN##*:}
				read SIZE 2> /dev/null < $BLOCKPATH/size || continue
				read SSIZE 2> /dev/null < $BLOCKPATH/queue/hw_sector_size
				if [[ -z $SSIZE ]] && [[ -b "/dev/$BLOCKNAME" ]]; then
					SSIZE=$(blockdev --getss /dev/$BLOCKNAME 2>/dev/null)
				fi
			fi
		else
			# BLOCKNAME for offline, alias and secondary devices
			# will not be printed, it's just a key for sorting
			if [[ "$ONLINE" == 0 ]]; then
				BLOCKNAME=""
			elif [[ "$COPYROLE" == "secondary" ]]; then
				BLOCKNAME="b"
			else
				BLOCKNAME="a"
			fi
			MAJMIN=
			MAJOR=
			MINOR=
			SIZE=
		fi

		# busid is the base name of the device path
		if [[ "$SHORTID" == "true" ]]; then
			BUSID=${DEVPATH##*.}
		else
			BUSID=${DEVPATH##*/}
		fi

		if [[ "$PRINTUID" == "true" ]]; then
			SORTKEYLEN=${#DEV_UID}
			SORTKEY=$DEV_UID
			FORMATTED_UID="$DEV_UID"
		else
			SORTKEYLEN=${#BLOCKNAME}
			SORTKEY=$BLOCKNAME
			FORMATTED_UID=""
		fi

		if [[ "$OUTPUT" == "old" ]]; then
			oldoutput
		elif [[ "$OUTPUT" == "extended" ]]; then
			extended
		elif [[ "$PRINTUID" == "true" ]]; then
			uid
		elif [[ "$OUTPUT" == "host" ]]; then
			host
		elif [[ "$OUTPUT" == "copy" ]]; then
			copy
		else
			newoutput
		fi
    done
}

function newoutput()
{
	#-------------------------------------------#
	# format data for output                    #
	#-------------------------------------------#

	if [[ "$ONLINE" == 0 ]]; then
		printf "%s:%s:%-8s  offline\n" \
			"$SORTKEYLEN" "$SORTKEY" \
			"$BUSID" ;
		return
	fi

	if [[ "$ALIAS" == 1 ]]; then
		if [[ "$BASEONLY" == "false" ]]; then
			printf "%s:%s:%-8s  alias %26s\n" \
				"$SORTKEYLEN" "$SORTKEY" \
				"$BUSID" \
				"$DISCIPLINE"
		fi
		return
	fi

	if [[ "$COPYROLE" == "secondary" ]]; then
		printf "%s:%s:%-8s  secondary %-8s %13s\n" \
		       "$SORTKEYLEN" "$SORTKEY" \
		       "$BUSID" \
		       "$PRIMARY_BLOCKNAME" \
		       "$DISCIPLINE"
		return
	fi

	if [[ "$READONLY" == 0 ]]; then
		ROSTRING=""
	else
		ROSTRING="(ro)"
	fi

	if [[ -z "$BLOCKNAME" ]] || [[ -z "$SIZE" ]]; then
		ACTIVE="active"
		BLOCKCOUNT=""
		MBSIZE=""
	elif [[ "$SIZE" == 0 ]]; then
		ACTIVE="n/f"
		BLOCKCOUNT=""
		MBSIZE=""
		SSIZE=""
	else
		if [[ -n "$SSIZE" ]] && [[ "$SSIZE" > 0 ]]; then
				BLOCKCOUNT=$(( SIZE / (SSIZE / 512) ))
		else
			SSIZE="???"
			BLOCKCOUNT="???"
		fi
		MBSIZE=$(( SIZE / 2048 ))MB
		ACTIVE="active"
	fi

	if [[ "$ESE" == 1 ]]; then
		DISCIPLINE="${DISCIPLINE} (ESE)"
	fi

	printf "%s:%s:%-8s  %-6s%-2s  %-8s  %-2s:%-2s   %-11s  %-4s   %-8s  %s\n" \
		"$SORTKEYLEN" "$SORTKEY" \
		"$BUSID" \
		"$ACTIVE" \
		"$ROSTRING" \
		"$BLOCKNAME" \
		"$MAJOR" \
		"$MINOR" \
		"$DISCIPLINE" \
		"$SSIZE" \
		"$MBSIZE" \
		"$BLOCKCOUNT" ;
}

function oldoutput()
{
	#-------------------------------------------#
	# format data for output                    #
	#-------------------------------------------#

	if [[ "$ONLINE" == 0 ]]; then
		printf "%s:%s:%s(%s)%s : offline\n" \
			"$SORTKEYLEN" "$SORTKEY" \
			"$BUSID" \
			"$DISCIPLINE" \
			"$FORMATTED_UID"
		return
	fi

	if [[ "$ALIAS" == 1 ]]; then
		if [[ "$BASEONLY" == "false" ]]; then
			printf "%s:%s:%s(%s)%s : alias\n" \
				"$SORTKEYLEN" "$SORTKEY" \
				"$BUSID" \
				"$DISCIPLINE" \
				"$FORMATTED_UID"
		fi
		return
	fi

	if [[ "$READONLY" == 0 ]]; then
		ROSTRING=""
	else
		ROSTRING="(ro)"
	fi

	printf "%s:%s:%s(%-4s)%s at (%3i:%3i) is %-7s%4s: " \
		"$SORTKEYLEN" "$SORTKEY" \
		"$BUSID" \
		"$DISCIPLINE" \
		"$FORMATTED_UID" \
		"$MAJOR" \
		"$MINOR" \
		"$BLOCKNAME" \
		"$ROSTRING" ;

	if [[ -z "$BLOCKNAME" ]] || [[ -z "$SIZE" ]]; then
		printf "active\n"
	elif [[ "$SIZE" == 0 ]]; then
		printf "n/f\n"
	else
		if [[ -n "$SSIZE" ]] && [[ "$SSIZE" > 0 ]]; then
				BLOCKCOUNT=$(( SIZE / (SSIZE / 512) ))
		else
			SSIZE="???"
			BLOCKCOUNT="???"
		fi
		MBSIZE=$(( SIZE / 2048 ))
		printf "active at blocksize %s, %s blocks, %i MB\n" \
			"$SSIZE" "$BLOCKCOUNT" "$MBSIZE"
	fi
}

function extended()
{
	PIM=0
	OPM=0
	NPPM=0
	CABLEPM=0
	CUIRPM=0
	HPFPM=0
	IFCCPM=0

	# additional information
	read DIAG 2> /dev/null < $DEVPATH/use_diag
	read EER 2> /dev/null < $DEVPATH/eer_enabled
	read ERP 2> /dev/null < $DEVPATH/erplog
	read HPF 2> /dev/null < $DEVPATH/hpf
        # in case the path_masks do not exist simply ignore it
	read OPM NPPM CABLEPM CUIRPM HPFPM IFCCPM 2> /dev/null < $DEVPATH/path_masks
	read -a C 2> /dev/null < $DEVPATH/../chpids
	read PIM PAM POM 2> /dev/null < $DEVPATH/../pimpampom
	read ESE 2> /dev/null < $DEVPATH/ese
	read EXTSZ 2> /dev/null < $DEVPATH/extent_pool/extent_size
	read CAPACITY 2> /dev/null < $DEVPATH/capacity/logical_capacity
	read ALLOCATED 2> /dev/null < $DEVPATH/capacity/space_allocated
	read FC_SEC 2> /dev/null < $DEVPATH/fc_security

	# convert to hexadecimal values
	PIM=0x$PIM
	OPM=0x$OPM
	NPPM=0x$NPPM
	CABLEPM=0x$CABLEPM
	CUIRPM=0x$CUIRPM
	HPFPM=0x$HPFPM
	IFCCPM=0x$IFCCPM

       	#-----------------------------------------------------------#
       	# aggregate chpids and path mask to useful information      #
       	#-----------------------------------------------------------#

        # initialise chpid lists
        INSTALLED_PATHS=(" " " " " " " " " " " " " " " ")
	USED_PATHS=(" " " " " " " " " " " " " " " ")
	NP_PATHS=(" " " " " " " " " " " " " " " ")
        CUIR_PATHS=(" " " " " " " " " " " " " " " ")
        CABLE_PATHS=(" " " " " " " " " " " " " " " ")
        HPF_PATHS=(" " " " " " " " " " " " " " " ")
        IFCC_PATHS=(" " " " " " " " " " " " " " " ")

	# installed paths
	j=0
	mask=0x80
	for (( i=0; i<8; i++ )) ;do
	    PM=$(($PIM&$mask))
	    if [ $PM -gt 0 ] ;then
		INSTALLED_PATHS[$j]=${C[$i]} ;
		((j++)) ;
	    fi
	    (( mask>>=1 ))
	done

	# used paths
	j=0
	mask=0x80
	for (( i=0; i<8; i++ )) ;do
	    PM=$(($OPM&$mask))
	    if [ $PM -gt 0 ] ;then
		USED_PATHS[$j]=${C[$i]} ;
		((j++)) ;
	    fi
	    (( mask>>=1 ))
	done

	# non preffered paths
	j=0
	mask=0x80
	for (( i=0; i<8; i++ )) ;do
	    PM=$(($NPPM&$mask))
	    if [ $PM -gt 0 ] ;then
		NP_PATHS[j]=${C[$i]} ;
		((j++)) ;
	    fi
	    (( mask>>=1 ))
	done

	# cuir quiesced paths
	j=0
	mask=0x80
	for (( i=0; i<8; i++ )) ;do
	    PM=$(($CUIRPM&$mask))
	    if [ $PM -gt 0 ] ;then
		CUIR_PATHS[j]=${C[$i]} ;
		((j++)) ;
	    fi
	    (( mask>>=1 ))
	done

	# mis cabled paths
	j=0
	mask=0x80
	for (( i=0; i<8; i++ )) ;do
	    PM=$(($CABLEPM&$mask))
	    if [ $PM -gt 0 ] ;then
		CABLE_PATHS[j]=${C[$i]} ;
		((j++)) ;
	    fi
	    (( mask>>=1 ))
	done

	# HPF unusable paths
	j=0
	mask=0x80
	for (( i=0; i<8; i++ )) ;do
	    PM=$(($HPFPM&$mask))
	    if [ $PM  -gt 0 ] ;then
		HPF_PATHS[j]=${C[$i]} ;
		((j++)) ;
	    fi
	    (( mask>>=1 ))
	done

	# IFCC unusable paths
	j=0
	mask=0x80
	for (( i=0; i<8; i++ )) ;do
	    PM=$(($IFCCPM&$mask))
	    if [ $PM  -gt 0 ] ;then
		IFCC_PATHS[j]=${C[$i]} ;
		((j++)) ;
	    fi
	    (( mask>>=1 ))
	done

       	#-------------------------------------------#
       	# format data for output                    #
       	#-------------------------------------------#

	if [[ "$ONLINE" == 0 ]]; then
		ACTIVE="offline"
		printf "%s:%s:%s#  status:\t\t\t\t%s#  use_diag:\t\t\t\t%s#  readonly:\t\t\t\t%s#  eer_enabled:\t\t\t\t%s#  erplog:\t\t\t\t%s#  hpf:\t\t\t\t\t%s#  uid:  \t\t\t\t%s#  paths_installed: \t\t\t%s %s %s %s %s %s %s %s#  paths_in_use: \t\t\t%s %s %s %s %s %s %s %s#  paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s#  paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s#  paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s#  paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s#  paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \
			"$SORTKEYLEN" "$SORTKEY" \
			"$BUSID" \
			"$ACTIVE" \
			"$DIAG" \
			"$READONLY" \
			"$EER" \
			"$ERP" \
			"$HPF" \
			"$DEV_UID" \
		        "${INSTALLED_PATHS[@]}" \
		        "${USED_PATHS[@]}" \
		        "${NP_PATHS[@]}" \
		        "${CABLE_PATHS[@]}" \
		        "${CUIR_PATHS[@]}" \
		        "${HPF_PATHS[@]}" \
		        "${IFCC_PATHS[@]}" ;
		return
	elif [[ "$ALIAS" == 1 ]] || [[ "$COPYROLE" == "secondary" ]]; then
		if [[ "$BASEONLY" == "false" ]] || [[ "$COPYROLE" == "secondary" ]]; then
			if [[ "$COPYROLE" == "secondary" ]]; then
				ACTIVE="secondary"
			else
				ACTIVE="alias"
			fi
			printf "%s:%s:%s#  status:\t\t\t\t%s#  type: \t\t\t\t%s#  use_diag:\t\t\t\t%s#  readonly:\t\t\t\t%s#  eer_enabled:\t\t\t\t%s#  erplog:\t\t\t\t%s#  hpf:\t\t\t\t\t%s #  uid:  \t\t\t\t%s#  fc_security: \t\t\t\t%s#  paths_installed: \t\t\t%s %s %s %s %s %s %s %s#  paths_in_use: \t\t\t%s %s %s %s %s %s %s %s#  paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s#  paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s#  paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s#  paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s#  paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#  copy_pairs:\t\t\t\t%s#\n" \
				"$SORTKEYLEN" "$SORTKEY" \
				"$BUSID" \
				"$ACTIVE" \
				"$DISCIPLINE" \
				"$DIAG" \
				"$READONLY" \
				"$EER" \
				"$ERP" \
				"$HPF" \
				"$DEV_UID" \
				"$FC_SEC" \
			        "${INSTALLED_PATHS[@]}" \
			        "${USED_PATHS[@]}" \
			        "${NP_PATHS[@]}" \
			        "${CABLE_PATHS[@]}" \
			        "${CUIR_PATHS[@]}" \
			        "${HPF_PATHS[@]}" \
		                "${IFCC_PATHS[@]}" \
		                "${COPYPAIR}" ;
		fi
		return
	elif [[ -z "$BLOCKNAME" ]] || [[ -z "$SIZE" ]]; then
		ACTIVE="active"
		COLON=""
	elif [[ "$SIZE" == 0 ]]; then
		ACTIVE="n/f"
		COLON=""
	else
		if [[ -n "$SSIZE" ]] && [[ "$SSIZE" > 0 ]]; then
			BLOCKCOUNT=$(( SIZE / (SSIZE / 512) ))
		else
			SSIZE="???"
			BLOCKCOUNT="???"
		fi
		MBSIZE=$(( SIZE / 2048 ))MB
		ACTIVE="active"
		COLON=":"
	fi

	if [[ "$ESE" == 1 ]]; then
		DISCIPLINE="${DISCIPLINE} (ESE)"
	fi

	printf "%s:%s:%s/%s/%s%s%s#  status:\t\t\t\t%s#  type: \t\t\t\t%s#  blksz:\t\t\t\t%s#  size: \t\t\t\t%s#  blocks:\t\t\t\t%s#  extent_size:\t\t\t\t%s#  logical_capacity:\t\t\t%s#  space_allocated:\t\t\t%s#  use_diag:\t\t\t\t%s#  readonly:\t\t\t\t%s#  eer_enabled:\t\t\t\t%s#  erplog:\t\t\t\t%s#  hpf:\t\t\t\t\t%s#  uid:  \t\t\t\t%s#  fc_security: \t\t\t\t%s#  paths_installed: \t\t\t%s %s %s %s %s %s %s %s#  paths_in_use: \t\t\t%s %s %s %s %s %s %s %s#  paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s#  paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s#  paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s#  paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s#  paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#  copy_pairs:\t\t\t\t%s#\n" \
		"$SORTKEYLEN" "$SORTKEY" \
		"$BUSID" \
		"$BLOCKNAME" \
		"$MAJOR" \
		"$COLON" \
		"$MINOR" \
		"$ACTIVE" \
		"$DISCIPLINE" \
		"$SSIZE" \
		"$MBSIZE" \
		"$BLOCKCOUNT" \
		"$EXTSZ" \
		"$CAPACITY" \
		"$ALLOCATED" \
		"$DIAG" \
		"$READONLY" \
		"$EER" \
		"$ERP" \
		"$HPF" \
		"$DEV_UID" \
		"$FC_SEC" \
	        "${INSTALLED_PATHS[@]}" \
	        "${USED_PATHS[@]}" \
	        "${NP_PATHS[@]}" \
	        "${CABLE_PATHS[@]}" \
	        "${CUIR_PATHS[@]}" \
	        "${HPF_PATHS[@]}"  \
	        "${IFCC_PATHS[@]}" \
		"${COPYPAIR}" ;
}

function host()
{
findDASDDebugfsDirectorie

if [[ ! -f "$DASD_DBF_DIR/$BUSID/host_access_list" ]]
then
    printf "\n%s: hosts access information not available\n" "$BUSID"
    return
fi

local temp=`mktemp /tmp/lsdasd.XXXXXX`
if test -w $temp ; then :; else
	printf "\nCreating temporary file failed\n"
	return
fi

cat $DASD_DBF_DIR/$BUSID/host_access_list > $temp 2> /dev/null
ret=$?
if [[ $ret -ne 0 ]]
then
    printf "%s: hosts access information not available\n" "$BUSID"
    rm -f $temp
    return $ret
fi

unset index
unset array
declare -a array

index=(pgid status_flags sysplex_name supported_cylinder timestamp)

for element in ${index[@]}
do
	count=0

	declare -a $element
	OLDIFS=$IFS
	IFS=$'\n'
	for value in `grep $element $temp`
	do
		(( ++count ))
		value=$(echo -e $value | cut -d ' ' -f2)
		eval $element[$count]=$value
	done
	IFS=$OLDIFS
done

printf "Host information for %s\n" "$BUSID";
printf "Path-Group-ID          LPAR  CPU   FL  Status  Sysplex      Max_Cyls        Time\n";
printf "================================================================================\n";

# mask bits for online and reserved state
online_reserved_mask=0xE0

# print name value lists
for i in `seq 1 $count`;
do
    # get flags field
    value=${status_flags[$i]}
    # mark as hex value
    value=0x$value
    # mask online and reserved bits
    value=$(($value & $online_reserved_mask))

    case $value in
	0 ) # 0x00
	    STATE="OFF"
	    ;;
	32 ) # 0x20
	    STATE="OFF-RSV"
	    ;;
	64 ) # 0x40
	    STATE="ON"
	    ;;
	96 ) # 0x60
	    STATE="ON-RSV"
	    ;;
	* )
	    STATE="-"
	    ;;
    esac

    printf "%22s %02s %07s  %02s  %-6s  %-8s  %11u  %10lu\n" \
	"${pgid[$i]}" \
	"${pgid[$i]:4:2}" \
	"${pgid[$i]:6:4}" \
	"${status_flags[$i]}" \
	"$STATE" \
	"${sysplex_name[$i]}" \
	"${supported_cylinder[$i]}" \
	"${timestamp[$i]}" \
	;
done
printf "\n";

rm -f $temp
}

# pad sortkey with given number zeroes
function padSortKey()
{
	local LEN=$1
	for (( i=0; i<$LEN; i++ ))
	do
		printf -v SORTKEY "%s0" $SORTKEY
	done
}

function copy()
{
	if [[ "$COPYROLE" == "none" ]]; then
		return
	fi

	SORTKEYLEN=$((${#PRIMARY_UID}+${#DEV_UID}))
	if [[ "$COPYROLE" == "secondary" ]]; then
		SORTKEY=$PRIMARY_UID$DEV_UID
		PAIREDDEVICES=$PRIMARY_UID
		BLOCKNAME=""
	else
		SORTKEY=$PRIMARY_UID
		padSortKey ${#DEV_UID}
		PAIREDDEVICES=$SECONDARY_LIST
	fi

	printf "%s:%s:%-8s  %-10s %-8s  %-8s %s  \n" \
	       "$SORTKEYLEN" "$SORTKEY" \
	       "$BUSID" \
	       "$COPYROLE" \
	       "$BLOCKNAME" \
	       "$PAIREDDEVICES";
}

function uid()
{
	#-------------------------------------------#
	# format data for output                    #
	#-------------------------------------------#

	if [[ "$ONLINE" == 0 ]]; then
		BLOCKNAME="offline"
	elif [[ "$ALIAS" == 1 ]]; then
		if [[ "$BASEONLY" == "true" ]]; then
			return
		else
			BLOCKNAME="alias"
		fi
	fi

	if [[ "$COPYROLE" == "secondary" ]]; then
		BLOCKNAME="secondary"
	fi

	printf "%s:%s:%-8s  %-10s  %s\n" \
		"$SORTKEYLEN" "$SORTKEY" \
		"$BUSID" \
		"$BLOCKNAME" \
		"$FORMATTED_UID" ;
}

SHORTID=false
PRINTOFFLINE=false
VERBOSE=false
PRINTUID=false
BASEONLY=false
OUTPUT="new"
#------------------------------------------------------------------------------
# Evaluating command line options
#------------------------------------------------------------------------------
while [ $# -gt 0 ]; do
	case $1 in
	--help|-h)
		PrintUsage
		exit 0
		;;
	--verbose|-v)
		VERBOSE=true
		;;
	--offline|-a)
		PRINTOFFLINE=true
		;;
	--short|-s)
		SHORTID=true
		;;
	--uid|-u)
		PRINTUID=true
		;;
	--base|-b)
		BASEONLY=true
		;;
	--compat|-c)
		OUTPUT="old"
		;;
	--long|-l)
		OUTPUT="extended"
		;;
	--host-access-list|-H)
		OUTPUT="host"
		;;
	--copy-pairs|-P)
		OUTPUT="copy"
		;;
	--version)
		PrintVersion
		exit 0
		;;
	-*)
		echo "$CMD: Invalid option $1"
		echo "Try 'lsdasd --help' for more information."
		exit 1
		;;
	*)
		DEV="$(CheckDeviceString $1)"
		if [ "$DEV" = "" ]; then
			echo "$CMD: ERROR: $1 no device format"
			exit 1
		fi
		if [ "$DEVLIST" == "" ]; then
			DEVLIST="$DEV"
		else
			DEVLIST="$DEVLIST\|$DEV"
		fi
		;;
	esac
	shift
	if [[ $OUTPUT == "extended" ]]; then
		if [[ $PRINTUID == true ]]; then
			echo "$CMD: ERROR: invalid options specified"
			exit 1
		fi
	fi
done


PROCESSING="listDASDDeviceDirectories "
# if there is a DEVLIST remove all elements not in the DEVLIST
if [ "$DEVLIST" != "" ]; then
	PROCESSING=" $PROCESSING | grep \"$DEVLIST\" "
fi

# gather information on devices in list
PROCESSING=" $PROCESSING | gatherDeviceData "

# sort resulting list
if [[ "$OUTPUT" == "host" ]]; then
    PROCESSING=" $PROCESSING"
else
    PROCESSING=" $PROCESSING | sort -t: -k1n -k2 | cut -d: -f3- "
fi

if  [[ "$PRINTUID" == "true" ]] && [[ "$OUTPUT" != "old" ]]; then
	printf "Bus-ID    Name      UID\n"
	printf "================================================================================\n"
elif [[ "$OUTPUT" == "new" ]]; then
	printf "Bus-ID    Status    Name      Device  Type         BlkSz  Size      Blocks\n"
	printf "================================================================================\n"
elif [[ "$OUTPUT" == "copy" ]]; then
	printf "Bus-ID    Role       Name      Paired devices\n";
	printf "================================================================================\n";
elif [[ "$OUTPUT" == "extended" ]]; then
	PROCESSING=" $PROCESSING | sed 's/#/\n/g' "
fi

#execute all steps
eval "$PROCESSING"

exit 0
