#!/bin/bash

set -e
#set -x

LOCKDIR=/var/run/lock
LOCKFILE=${LOCKDIR}/$(basename ${0})
if [ "${1}" = "--nolock" ] ; then
        shift
else
        logger -t "udev" "===> Claming lock for $0 in ${LOCKFILE}"
        if ! flock -w 120 -x ${LOCKFILE} $0 --nolock $@ ; then
                exit 1
        fi
        exit 0
fi

logger -t "udev" "===> DEVPATH=${DEVPATH} ID_BUS=${ID_BUS}"

# Script param (sent as env var):
# ID_BUS (example: scsi, ata)
# DEVPATH (example: /devices/pci0000:80/0000:80:08.2/0000:81:00.0/ata8/host7/target7:0:0/7:0:0:0/block/sdc)
#
# In fact, we see the variables as per:
# udevadm test -a -p  $(udevadm info -q path -n /dev/sdc)

CHASSIS_MANUFACTURER=$(dmidecode -s system-manufacturer)
CHASSIS_MODEL=$(dmidecode -s system-product-name)
CACHE_FILE=/run/oci-hdd-cache

case "${CHASSIS_MANUFACTURER}" in
"LinuxKVM")
	case "${CHASSIS_MODEL}" in
	"qemu-oci")
		if [ -n "$ID_BUS" ] && [ "$ID_BUS" = "scsi" ]; then
			card=$(echo $DEVPATH | cut -d/ -f5)
			target=$(echo $DEVPATH | cut -d/ -f8)
			if [ "$card" = "virtio0" ] ; then
				case "${target}" in
				"2:0:0:0") disk="sda" ;;
				"2:0:1:0") disk="sdb" ;;
				"2:0:2:0") disk="sdc" ;;
				"2:0:3:0") disk="sdd" ;;
				"2:0:4:0") disk="sde" ;;
				"2:0:5:0") disk="sdf" ;;
				"2:0:6:0") disk="sdg" ;;
				"2:0:7:0") disk="sdh" ;;
				"2:0:8:0") disk="sdi" ;;
				"2:0:9:0") disk="sdj" ;;
				*) logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1 ;;
				esac
				logger -t "udev" "Adding symlink $disk for device $card-$target"
				echo $disk
				exit 0
			else
				logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
			fi
		else
			logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
		fi
	;;
	*)
		logger -t "udev" "Qemu chassis not supported, exit."
		exit 1
	;;
	esac
;;
"Dell Inc.")
	case "${CHASSIS_MODEL}" in
	"PowerEdge R410")
		# Here we have 2 internal SSDs on CDROM link and 4 SATA HDD
		if [ -n "$ID_BUS" ] && [ "$ID_BUS" = "ata" ]; then
			card=$(echo $DEVPATH | cut -d/ -f5)
			target=$(echo $DEVPATH | cut -d/ -f8)
			if   [ "$card" = "ata1" ] && [ "$target" = "0:0:0:0" ]; then disk="sda"
			elif [ "$card" = "ata1" ] && [ "$target" = "0:1:0:0" ]; then disk="sdb"
			elif [ "$card" = "ata2" ] && [ "$target" = "1:0:0:0" ]; then disk="sdc"
			elif [ "$card" = "ata3" ] && [ "$target" = "2:0:0:0" ]; then disk="sdd"
			elif [ "$card" = "ata4" ] && [ "$target" = "3:0:0:0" ]; then disk="sde"
			elif [ "$card" = "ata5" ] && [ "$target" = "4:0:0:0" ]; then disk="sdf"
			else logger -t "udev" "Device $DEVPATH detected but not supported."; exit 1
			fi
		else    
			logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
		fi
		logger -t "udev" "Adding symlink $disk for device $card-$target"
		echo "$disk"
		exit 0
	;;
	"PowerEdge R610"|"PowerEdge R640")
		if [ -n "$ID_BUS" ] && [ "$ID_BUS" = "scsi" ]; then
			target=$(echo $DEVPATH | cut -d/ -f8)
			if   [ "$target" = "0:2:0:0" ]; then disk="sda"
			elif [ "$target" = "0:2:1:0" ]; then disk="sdb"
			elif [ "$target" = "0:2:2:0" ]; then disk="sdc"
			elif [ "$target" = "0:2:3:0" ]; then disk="sdd"
			elif [ "$target" = "0:2:4:0" ]; then disk="sde"
			elif [ "$target" = "0:2:5:0" ]; then disk="sdf"
			else logger -t "udev" "Device $target detected but not supported."; exit 1
			fi
		else
			logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
		fi
		logger -t "udev" "Adding symlink $disk for device $card-$target"
		echo "$disk"
		exit 0
	;;
	"PowerEdge R740xd")
		# Only versions without hardware RAID PERC controller
		# We expect here 2 SATA SSDs and 12 SAS HDD

		usecache=false
		# read a cache file, if exist and recent
		if [ -f $CACHE_FILE ]; then
			lastentry=$(stat -c %Y $CACHE_FILE)
			lastvalid=$(date -d "now -3 min" +%s)
			if [ $lastentry -gt $lastvalid ]; then
				usecache=true
			else
				rm $CACHE_FILE
			fi
		elif [ -e $CACHE_FILE ]; then
			rm $CACHE_FILE
		fi
		# cache is invalid
		if ! $usecache; then
			# Rebuild cache with PERC CLI
			# Output in json
			perccli /c0 show all nolog J > $CACHE_FILE
			logger -t "udev" "[Cache built]"
		else
			logger -t "udev" "[Using cache]"
		fi
		disk=""

		# Parse the output of "perccli64 /c0 show all nolog J"
		# and build a temporary cache file...

		# WWN ID in udev is hex +3 compare to WWN in perccli
		logger -t "udev" "ID_WWN: $ID_WWN"
		printf -v WWN "%X" $(($ID_WWN - 0x3))
		logger -t "udev" "WWN: $WWN"

		# Get disk json key from WWN
		disk_key=$(cat $CACHE_FILE | jq -r "[.Controllers[].\"Response Data\".\"Physical Device Information\"[]|select(..|.WWN? == \"$WWN\")|to_entries[]|select(.key|startswith(\"Drive\")).key][0]")
		logger -t "udev" "disk_key: $disk_key"

		if [ "$disk_key" == "null" ]; then
			# Let's try original WWN
			printf -v WWN "%X" $ID_WWN
			logger -t "udev" "WWN: $WWN"
			disk_key=$(cat $CACHE_FILE | jq -r "[.Controllers[].\"Response Data\".\"Physical Device Information\"[]|select(..|.WWN? == \"$WWN\")|to_entries[]|select(.key|startswith(\"Drive\")).key][0]")
			logger -t "udev" "disk_key: $disk_key"
			if [ "$disk_key" == "null" ]; then
				# Last try last
				printf -v WWN "%X" $(($ID_WWN + 0x3))
				logger -t "udev" "WWN: $WWN"
				disk_key=$(cat $CACHE_FILE | jq -r "[.Controllers[].\"Response Data\".\"Physical Device Information\"[]|select(..|.WWN? == \"$WWN\")|to_entries[]|select(.key|startswith(\"Drive\")).key][0]")
				logger -t "udev" "disk_key: $disk_key"
			fi
		fi

		PHYSDRIVE=$(echo "$disk_key" | awk 'BEGIN { FS="[/ ]" } { print $5 }')
		logger -t "udev" "PHYSDRIVE: $PHYSDRIVE"
		case "${PHYSDRIVE}" in
			# The format is:
			# ${BAY}
			# Cause we strip controller and enclusure just before
			"s0") PHYDISK="sda" ;;
			"s1") PHYDISK="sdb" ;;
			"s2") PHYDISK="sdc" ;;
			"s3") PHYDISK="sdd" ;;
			"s4") PHYDISK="sde" ;;
			"s5") PHYDISK="sdf" ;;
			"s6") PHYDISK="sdg" ;;
			"s7") PHYDISK="sdh" ;;
			"s8") PHYDISK="sdi" ;;
			"s9") PHYDISK="sdj" ;;
			"s10") PHYDISK="sdk" ;;
			"s11") PHYDISK="sdl" ;;
			"s12") PHYDISK="sdm" ;;
			"s13") PHYDISK="sdn" ;;
		esac

		if ! [ -n "$PHYDISK" ]; then
			logger -t "udev" "Device $DEVPATH detected but not found."
			exit 1
		fi

		logger -t "udev" "Adding symlink $PHYDISK for device $DEVPATH"
		echo "$PHYDISK"
		exit 0
	;;
	*)
		logger -t "udev" "Dell chassis not supported, exit."
		exit 1
	;;
	esac
;;
"HPE")
	case "${CHASSIS_MODEL}" in
	"ProLiant DL385 Gen10 Plus")
		card=$(echo $DEVPATH | cut -d/ -f6)
		target=$(echo $DEVPATH | awk -F '/' '{ print $9 }')
		# We expect here 2 SATA SSDs and 12 SAS HDD
		if [ "${card}" = "ata7" ] && [ "${target}" = "6:0:0:0" ]; then
			disk="sda"
		elif [ "${card}" = "ata8" ] && [ "${target}" = "7:0:0:0" ]; then
			disk="sdb"
		elif   [ "${card}" = "host8" ] ; then
			usecache=false
			# read a cache file, if exist and recent
			if [ -f $CACHE_FILE ]; then
				lastentry=$(stat -c %Y $CACHE_FILE)
				lastvalid=$(date -d "now -3 min" +%s)
				if [ $lastentry -gt $lastvalid ]; then
					usecache=true
				else
					rm $CACHE_FILE
				fi
			elif [ -e $CACHE_FILE ]; then
				rm $CACHE_FILE
			fi
			# cache is recent and a file
			if $usecache; then
				# Just read from cache file...
				disk=$(grep $DEVNAME $CACHE_FILE | awk '{ print $2 }')
				if ! [ -n "$disk" ]; then
					logger -t "udev" "[Cache mode] Device $DEVPATH detected but not found."
					exit 1
				fi
			else
				disk=""

				# Parse the output of "ssacli controller slot=0 physicaldrive all show detail"
				# and build a cache file...
				TMPFILE=$(mktemp -t parse-hpe-drives.XXXXXX)
				# The grep here just helps because we later have less lines to parse.
				SLOT_NUM=$(ssacli controller all show | head -n 2 | tail -n 1 | sed -r 's/.*(Slot [0-9]+).*/\1/' | sed 's/Slot //')
				ssacli controller slot=${SLOT_NUM} physicaldrive all show detail | grep -E 'physicaldrive|Disk Name: /dev/' >$TMPFILE
				while IFS='' read -r LINE || [ -n "${LINE}" ]; do
					if echo ${LINE} | grep -q physicaldrive ; then
						PHYSDRIVE=$(echo ${LINE} | sed 's/^.*physicaldrive //')
						case "${PHYSDRIVE}" in
							# The format is:
							# ${SAS_PORT}:${BOX}:${BAY}
							# Since we don't care about ports, but where the drive is physically located
							# we use ${BOX}:${BAY}, so the physical location of the drives is deterministic
							# and we don't care the order of the ports.
							"1I:1:1"|"2I:1:1"|"3I:1:1") PHYDISK="sdc" ;;
							"1I:1:2"|"2I:1:2"|"3I:1:2") PHYDISK="sdd" ;;
							"1I:1:3"|"2I:1:3"|"3I:1:3") PHYDISK="sde" ;;
							"1I:1:4"|"2I:1:4"|"3I:1:4") PHYDISK="sdf" ;;
							"1I:2:1"|"2I:2:1"|"3I:2:1") PHYDISK="sdg" ;;
							"1I:2:2"|"2I:2:2"|"3I:2:2") PHYDISK="sdh" ;;
							"1I:2:3"|"2I:2:3"|"3I:2:3") PHYDISK="sdi" ;;
							"1I:2:4"|"2I:2:4"|"3I:2:4") PHYDISK="sdj" ;;
							"1I:3:1"|"2I:3:1"|"3I:3:1") PHYDISK="sdk" ;;
							"1I:3:2"|"2I:3:2"|"3I:3:2") PHYDISK="sdl" ;;
							"1I:3:3"|"2I:3:3"|"3I:3:3") PHYDISK="sdm" ;;
							"1I:3:4"|"2I:3:4"|"3I:3:4") PHYDISK="sdn" ;;
						esac
					elif echo ${LINE} | grep -q 'Disk Name: /dev/' ; then
						MY_DEV_NAME=$(echo ${LINE} | sed 's#^.*Disk Name: ##')
						if [ -n "${PHYDISK}" ] ; then
							echo "$MY_DEV_NAME $PHYDISK" >> $CACHE_FILE
							unset PHYDISK
						fi
						unset MY_DEV_NAME
					fi
				done <$TMPFILE
				rm -f ${TMPFILE}

				# Once it's built, just read from the cache file.
				disk=$(grep $DEVNAME $CACHE_FILE | awk '{ print $2 }')
				if ! [ -n "$disk" ]; then
					logger -t "udev" "[Normal mode] Device $DEVPATH detected but not found."
					exit 1
				fi
			fi
		else
			logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
		fi
		logger -t "udev" "Adding symlink $disk for device $DEVPATH"
		echo "$disk"
		exit 0
	;;
	*)
		logger -t "udev" "HPE chassis not supported, exit."
		exit 1
	;;
	esac
;;
*)
	logger -t "udev" "Chassis not supported, exit."
	exit 1
;;
esac
