#!/bin/bash
# SPDX-License-Identifier: MIT
#
# dasd - collect state for dasd resources.
#
# Copyright IBM Corp. 2023
#
# Usage: dasd [<datafile>]
#
# If a YAML datafile was specified, determine characteristics of all mentioned
# resources. Otherwise list all available resources.
#

DATAFILE="$1"

LIBEXEC="${TELA_FRAMEWORK:-$(readlink -f $(dirname $0)/../../..)}"/src/libexec

source $LIBEXEC/lib/common.bash || exit 1

CCWDRV="/sys/bus/ccw/drivers"
UIDS=todo

shopt -s nullglob

function get_dasd_dir() {
	local _var="$1" _id="$2" _dir

	for _dir in $CCWDRV/dasd-*/$_id ; do
		eval "$_var=$_dir"
		break
	done
}

function check_block_dev() {
	local dir="$1" var="$2" formatted="$3" bdev size blksize

	[[ -e "$dir" ]] || return

	bdev=${dir##*/}
	bdev=${bdev#block:}

	echo "  block_dev:"
	echo "    bdev: $bdev"
	eval "$var=$bdev"

	[[ "$formatted" == 1 ]] || return

	# size in sectors (512 byte)
	read size <$dir/size
	(( size=size*512 ))

	if [[ -e "$bdevdir/queue/logical_block_size" ]] ; then
		read blksize <"$dir/queue/logical_block_size"
	else
		# Fall back to blockdev but this requires root
		blksize=$(blockdev --getss /dev/$bdev 2>/dev/null)
	fi

	echo "    size: $size"
	echo "    blksize: $blksize"

}

# Record uid prefix for all alias devices
function enumerate_alias_uids() {
	local sysfs alias uid IFS

	UIDS=()
	for sysfs in $CCWDRV/dasd-*/[0-9a-f]*.*.* ; do
		read alias <$sysfs/alias
		[[ "$alias" == "1" ]] || continue

		read uid <$sysfs/uid

		IFS="."
		set -- $uid
		unset IFS

		if [[ "$4" == "xx" ]] ; then
			# HyperPAV alias
			UIDS[${#UIDS[@]}]="$1.$2.$3"
		else
			# PAV alias
			UIDS[${#UIDS[@]}]="$1.$2.$3.$4"
		fi
	done
}

function check_alias_count() {
	local uid="$1" alias_uid count=0

	[[ "$UIDS" == "todo" ]] && enumerate_alias_uids

	for alias_uid in ${UIDS[@]} ; do
		[[ $uid =~ ^${alias_uid} ]] && (( count=count+1 ))
	done

	echo "  alias_count: $count"
}

function check_uid() {
	local sysfs="$1" uid IFS vendor serial ssid ua vduit

	read uid 2>/dev/null <$sysfs/uid

	[[ -n "$uid" ]] || return

	IFS="."
	set -- $uid
	unset IFS
	vendor=$1
	serial=$2
	ssid=$3
	ua=$4
	vduit=$5

	echo "  uid:"
	echo "    id: $uid"
	echo "    vendor: $vendor"
	echo "    serial: $serial"
	echo "    ssid: $ssid"
	echo "    ua: $ua"
	[[ -n "$vduit" ]] && echo "    vduit: $vduit"

	check_alias_count $uid
}

function check_dasd_details() {
	local bdev="$1" IFS line format cyl

	# Parse dasdview output
	IFS=$'\n'
	while read -r line ; do
		case $line in
		"format"*)
			format=${line% formatted}
			format=${format##*$'\t'}
			[[ "$format" == "NOT" ]] && format="none"
			;;
		"number of cylinders"*)
			cyl=${line##* }
			;;
		esac
	done < <(dasdview -x /dev/$bdev 2>/dev/null)

	# Create output
	[[ -n "$format" ]] && echo "  format: $format" | tr "[A-Z]" "[a-z]"
	[[ -n "$cyl" ]] && echo "  cylinder_count: $cyl"
}

function check_dasd() {
	local id="$1" busid sysfs alias online status formatted=1 type bdev_name
	local fc_security="-"
	local cutype devtype

	canonical_ccwdev_id busid $id || return 1
	get_dasd_dir sysfs $busid

	# No output if DASD doesn't exist
	[[ -e "$sysfs" ]] || return 1

	# Filter out DASD alias devices
	read alias <$sysfs/alias
	[[ "$alias" == 1 ]] && return 1

	# Get additional data
	read online <$sysfs/online
	read status <$sysfs/status
	[[ "$status" == "unformatted" ]] && formatted=0
	type=${sysfs#*dasd-}
	type=${type%%/*}
	read cutype <$sysfs/cutype
	read devtype <$sysfs/devtype

	# Create output
	echo "dasd $busid:"
	echo "  busid: $busid"
	echo "  type: $type"
	echo "  sysfs: $sysfs"
	echo "  online: $online"
	echo "  cutype: $cutype"
	echo "  devtype: $devtype"

	check_uid $sysfs
	check_ccwdev_chpids "$busid" "  " "_tela_copy ../../chpid " 1 \
			    "chpid_count"

        # Block device information
	for bdevdir in $sysfs/block/* $sysfs/block:* ; do
		check_block_dev $bdevdir bdev_name $formatted
		break
	done

	[[ -n "$bdev_name" ]] && check_dasd_details $bdev_name
	read fc_security 2>/dev/null <"$sysfs/fc_security"
	echo "  fc_security: ${fc_security,,}"

	# Default values for meta-attributes
	echo "  allow_write: 0"

	return 0
}

function get_state() {
	local IFS=$'\n' count=0

	while read -r line ; do
		[[ ${line:0:5} != "dasd " ]] && continue
		id=${line:5}
		id="${id%:}"

		check_dasd "$id" && (( count=count+1 ))
	done
	echo "dasd_count_available: $count"
}

function get_all() {
	local do_list=$1 sysfs alias id count=0

	for sysfs in $CCWDRV/dasd-*/[0-9a-f]*.*.* ; do
		read alias <$sysfs/alias
		[[ "$alias" == "1" ]] && continue

		id=${sysfs##*/}
		(( count=count+1 ))
		[[ $do_list == 1 ]] && echo "dasd $id:"
	done

	[[ $do_list == 0 ]] && echo "dasd_count_total: $count"
}

if [[ -n "$DATAFILE" ]] ; then
	# Get state for resources specified in datafile
	get_state <"$DATAFILE"
	get_all 0
else
	# Get list of all available resources
	get_all 1
fi

exit 0
