#!/bin/bash

CTL_TOOL="vstorage-iscsi"
ISCSI_ETC="/etc/vstorage/iscsi"
ISCSI_SHM="/dev/shm/vstorage-iscsi"
ISCSI_TMP="/dev/shm/vstorage-iscsi"
PSTORAGE_ETC="/etc/vstorage"
PLOOP="/usr/sbin/ploop"
SHAMAN="/usr/sbin/shaman"
VSTORAGE_BIN="/usr/bin/vstorage"
SYSTEMD_TOOL="/usr/bin/systemctl"

TARGET_NO_OWNER="unregistered"

SCSI_TGT=${SCSI_TGT:-tgtd}
SCSI_TRANSPORT=${SCSI_TRANSPORT:-iscsi}

source /usr/libexec/vstorage-iscsi/vstorage-scsi-target-${SCSI_TGT}
source /usr/libexec/vstorage-iscsi/${SCSI_TRANSPORT}_functions

function is_root_on_vstorage {
	for d in $(cat /proc/mounts | awk '/^[pv]storage:\/\//{ print $2}'); do
		[[ "$ISCSI_ROOT" == $d* ]] && return 1
	done
	return 0
}

function pcs_iscsi_set_storage_attr {
	local dir="$1"; shift
	$VSTORAGE_BIN "set-attr" "$dir" ${@} >/dev/null 2>&1
	if [ $? -ne 0 ]; then
		echo "Can't set storage attributes for $dir" 1>&2
		return 1
	fi
	return 0
}

function get_host_id {
	cat $PSTORAGE_ETC/host_id
	rc=$?
	if [ $? -ne 0 ] ; then
		echo "Can't read host id" 1>&2
		return $rc
	fi
	return 0
}

function pcs_iscsi_is_registered_local {
	local target="$1"
	local host_id="$2"
	local owner=""
	if [ -z "$host_id" ] ; then
		host_id=`get_host_id`
		[ $? -ne 0 ] && return 2
	fi

	owner=`cat $ISCSI_ROOT/$target/control/host`
	if [ $? -ne 0 ] ; then
		echo "Unable read file $target/control/host" 1>&2
		return 3
	fi

	[ -z "$owner" -o "$owner" != "$host_id" ] && return 1

	return 0
}

function get_id {
	[ ! -d $ISCSI_SHM ] && mkdir -p $ISCSI_SHM

	(
	flock -x 200
	last_tid=`ls $ISCSI_SHM 2>/dev/null | sort -g -r | head -n 1`
	if [ -z "$last_tid" ] ; then
		last_tid="10"
	else
		last_tid=$[ $last_tid + 1]
	fi
	touch $ISCSI_SHM/$last_tid /dev/null 2>&1
	echo $last_tid
	) 200>$ISCSI_SHM/.lock
}

function pcs_iscsi_init_host {
	[ ! -d $ISCSI_SHM ] && mkdir -p $ISCSI_SHM/.running >/dev/null 2>&1

	${SCSI_TRANSPORT}_init_host

	return $?
}

function put_id {
	local tid="$1"
	local target="$2"
	(
		flock -x 201
		rm -f $ISCSI_SHM/$tid
	) 201>$ISCSI_SHM/.lock
	[ -n "$target" ] && rm -f "$ISCSI_SHM/.running/$target"
}

function pcs_iscsi_list_registered {
	host_id=`get_host_id`
	[ $? -ne 0 ] && return 1

	ls $ISCSI_ROOT 2>/dev/null | grep "^iqn*" 2>/dev/null | while read target
	do
		[ ! -d  "$ISCSI_ROOT/$target/control" ] && continue

		pcs_iscsi_is_registered_local $target $host_id >/dev/null 2>&1
		[ $? -ne 0 ] && continue
		echo ""
		echo "iscsi-$target"
		echo "$ISCSI_ROOT/$target"
	done
}

function pcs_iscsi_target_has_owner {
	local target="$1"
	local owner=""
	if [ -f "$ISCSI_ROOT/$target/control/host" ]; then
		owner=`cat $ISCSI_ROOT/$target/control/host`
		if [ $? -ne 0 ] ; then
			echo "Unable read file $ISCSI_ROOT/$target/control/host" 1>&2
			return 3
		fi
	fi

	[ -n "$owner" -a "$owner" != "$TARGET_NO_OWNER" ] && return 1
	return 0
}

function pcs_iscsi_bind_account {
	local target="$1"
	local ctl_port="$2"
	local account=""
	[ ! -L "$ISCSI_ROOT/$target/control/account" ] && return
	account=`/bin/readlink "$ISCSI_ROOT/$target/control/account"`
	if [ -n "$account" ] ; then
		local user=`basename $account`
		local passw=`cat "$account/auth" `
		${SCSI_TGT}_add_account target=$target user=$user passw=$passw ctl_port=$ctl_port
		return $?
	fi
	return 0
}

function pcs_iscsi_unregister_target_ha {
	local target="$1"
	msg=`echo "$TARGET_NO_OWNER" | dd of="$ISCSI_ROOT/$target/control/host" conv=fsync 2>&1`
	if [ $? -ne 0 ]; then
		echo "$msg" 1>&2
		echo "Can't write in $ISCSI_ROOT/$target/control/host" 1>&2
		return 2
	fi
	return 0
}

function pcs_iscsi_unregister_target {
	local target="$1"

	host_id=`get_host_id`
	[ $? -ne 0 ] && return 1

	prev_owner=`cat "$ISCSI_ROOT/$target/control/host" 2>/dev/null`

	pcs_iscsi_unregister_target_ha "$target"
	[ $? -ne 0 ] && return 3

	[ "$prev_owner" = "$host_id" ] && $SHAMAN del "iscsi-$target" >/dev/null 2>&1
	return 0
}

function pcs_iscsi_register_target_ha {
	local target="$1"
	local host_id=`get_host_id`
	[ $? -ne 0 ] && return 1

	# save host id in $ISCSI_ROOT/$target/control/host
	msg=`echo $host_id | /bin/dd of="$ISCSI_ROOT/$target/control/host" conv=fsync 2>&1`
	if [ $? -ne 0 ]; then
		echo "$msg" 1>&2
		echo "Unable save host_id in $ISCSI_ROOT/$target/control/host" 1>&2
		return 2
	fi

	# reset running state after registration
	rm -f "$ISCSI_ROOT/$target/control/.running"
	return 0
}

function pcs_iscsi_register_target {
	local target="$1"
	local prev_owner=""

	if [ -f "$ISCSI_ROOT/$target/control/host" ]; then
		prev_owner=`cat "$ISCSI_ROOT/$target/control/host"`
		if [ $? -ne 0 ] ; then
			echo "Unable read $ISCSI_ROOT/$target/control/host" 1>&2
			return 1
		fi
	fi

	pcs_iscsi_register_target_ha "$target"
	[ $? -ne 0 ] && return $?

	if [ -z "$prev_owner" -o "$prev_owner" = "$TARGET_NO_OWNER" ]; then
		$SHAMAN -i add "iscsi-$target" -P $ISCSI_ROOT/$target >/dev/null
		if [ $? -ne 0 ] ; then
			echo "Can't register target '$target' as shaman's resource" 1>&2
			echo "Leave target in unregistered state." 1>&2
			pcs_iscsi_unregister_target "$target" >/dev/null 2>&1
			return 2
		fi
	else
		$SHAMAN move-from "iscsi-$target" "md.$prev_owner" >/dev/null
		if [ $? -ne 0 ] ; then
			echo "Can't register target '$target' as shaman's resource" 1>&2
			echo "Leave target in unregistered state." 1>&2
			pcs_iscsi_unregister_target "$target" >/dev/null 2>&1
			return 3
		fi
	fi

	return 0
}

function pcs_iscsi_attach_lun {
	local ctl_port="$1"
	local lun_id="$2"
	local ploop_dev="$3"
	local serial_sn="$4"
	local target="$5"

	${SCSI_TGT}_attach_lun target=$target dev=$ploop_dev lun_id=$lun_id serial_sn=$serial_sn ctl_port=$ctl_port
	rc=$?

	return $rc
}

function pcs_iscsi_get_ploop_dev {
	local target="$1"
	local lun="$2"
	local lun_id=`expr "$lun" : "^lun\([0-9]*\)" `
	local canon_path=$(readlink -f "$ISCSI_ROOT/$target/$lun/ploop")
	local ploop_dev=$( $PLOOP list | grep "$canon_path" | cut -d ' ' -f 1)
	if [ -z "$ploop_dev" ] ; then
		echo "Unable find device for $ISCSI_ROOT/$target/$lun/ploop" 1>&2
		return 1
	fi

	if [ ! -b "/dev/$ploop_dev" ] ; then
		echo "Unable find block device /dev/$ploop_dev" 1>&2
		return 2
	fi
	echo "/dev/$ploop_dev"
	return 0
}

function pcs_iscsi_grow_lun {
	local target="$1"
	local lun="$2"
	local new_size="$3"
	local lun_id=`expr "$lun" : "^lun\([0-9]*\)" `
	local serial_sn=""
	local ploop_dev=""
	local ctl_port=""

	if [ ! -d "$ISCSI_ROOT/$target/$lun" ] ; then
		echo "LUN $lun_id for target $target doesn't exist" 1>&2
		return 1
	fi

	ctl_port=`pcs_iscsi_get_ctl_port $target`
	if [ -n "$ctl_port" ] ; then
		ploop_dev=`pcs_iscsi_get_ploop_dev $target $lun`
		[ $? -ne 0 ] && return 2
	fi

	# grow ploop
	msg=`$PLOOP grow -s $new_size "$ISCSI_ROOT/$target/$lun/DiskDescriptor.xml" 2>&1`
	if [ $? -ne 0 ] ; then
		echo "$msg" 1>&2
		echo "Can't grow ploop $ISCSI_ROOT/$target/$lun/ploop" 1>&2
		return 3
	fi

	if [ -n "$ploop_dev" ] ; then
		${SCSI_TGT}_rescan_lun target=$target lun_id=$lun_id dev=$ploop_dev ctl_port=$ctl_port
		return $?
	fi

	return 0
}

function pcs_iscsi_up_lun {
	local target="$1"
	local ctl_port="$2"
	local lun="$3"
	local dev=""
	local msg=""
	local serial_sn=""
	local lun_id=`expr "$lun" : "^lun\([0-9]*\)" `

	if [ ! -d "$ISCSI_ROOT/$target/$lun" ] ; then
		echo "LUN $lun_id for target $target doesn't exist" 1>&2
		return 1
	fi

	msg=`$PLOOP mount "$ISCSI_ROOT/$target/$lun/DiskDescriptor.xml" 2>&1`
	if [ $? -ne 0 ] ; then
		echo "$msg" 2>&1
		echo "Can't mount ploop $ISCSI_ROOT/$target/$lun/ploop" 1>&2
		return 2
	fi

	dev=`pcs_iscsi_get_ploop_dev $target $lun`
	[ $? -ne 0 ] && return 3

	[ -f "$ISCSI_ROOT/$target/$lun/serial_sn" ] && serial_sn=`cat "$ISCSI_ROOT/$target/$lun/serial_sn" 2>/dev/null`
	pcs_iscsi_attach_lun $ctl_port $lun_id $dev "$serial_sn" $target
	if [ $? -ne 0 ] ; then
		$PLOOP umount "$ISCSI_ROOT/$target/$lun/ploop" >/dev/null 2>&1
		return 4
	fi

	return 0
}

# stop running target
function pcs_iscsi_down_target {
	local target="$1"
	local ctl_port="$2"
	local force="$3"
	local luns=""

	pcs_iscsi_is_registered_local "$target"
	rc=$?
	if [ $rc -ne 0 ] ; then
		[ $rc -eq 1 ] && echo "Target $target not registered on current host" 1>&2
		return $rc
	fi

	if [ -z "$ctl_port" ]; then
		if [ ! -f "$ISCSI_SHM/.running/$target" ] ; then
			echo "Target $target not running" 1>&2
			return 0
		fi
		ctl_port=`cat $ISCSI_SHM/.running/$target`
	fi

	if [ -z "$ctl_port" ]; then
		echo "Control port is unknown" 1>&2
		return 1
	fi

	if [ -z "$force" ]; then
		# try failing early with a diagnostics on non-forced
		# stop, leaving the target online if there are active
		# connections.
		pcs_iscsi_target_can_stop_nonforcibly "$ctl_port" "$target"
		rc=$?
		if [ $rc -ne 0 ] ; then
			return $rc
		fi
	fi

	${SCSI_TGT}_delete_target target=$target force=$force ctl_port=$ctl_port
	rc=$?
	[ $rc -ne 0 ] && return $rc

	if [ -f "$ISCSI_ROOT/$target/control/address" ] ; then
		addresses=`cat "$ISCSI_ROOT/$target/control/address"`
		${SCSI_TRANSPORT}_remove_addr $addresses
	fi

	luns=`ls -d "$ISCSI_ROOT/$target"/lun[0-9]* 2>/dev/null`
	if [ -n "$luns" ] ; then
		for l in $luns; do
			ploop_full_path="$ISCSI_ROOT/$target/$(basename $l)/ploop"
			ploop_dev_name=`ploop list | grep "${ploop_full_path}" | cut -d ' ' -f1`
			${SCSI_TGT}_detach_dev dev=$ploop_dev_name
			$PLOOP umount "${ploop_full_path}" >/dev/null 2>&1
		done
	fi

	rm -f "$ISCSI_ROOT/$target/control/.running"
	put_id $ctl_port $target
	return 0
}

function pcs_iscsi_down_lun {
	local target="$1"
	local lun_id="$2"
	local ctl_port="$3"

	if [ -z "$ctl_port" ] ; then
		echo "Unknown control port"
		return 1
	fi

	${SCSI_TGT}_detach_lun target=$target lun_id=$lun_id ctl_port=$ctl_port
	if [ $? -ne 0 ] ; then
		echo "Can't delete LUN $lun_id for target $target" 1>&2
		return 2
	fi

	$PLOOP umount "$ISCSI_ROOT/$target/lun${lun_id}/ploop" >/dev/null
	ret=$?
	if [ $ret -ne 0 ] ; then
		echo "Failed to umount ploop image $ISCSI_ROOT/$target/lun${lun_id}/ploop" 1>&2
		return 3
	fi

	return 0
}

function pcs_iscsi_check_portals {
	local target="$1"
	local ctl_port="$2"

	[ ! -f "$ISCSI_ROOT/$target/control/address" ] && return 0
	addresses=`cat "$ISCSI_ROOT/$target/control/address" 2>/dev/null`
	for addr in `cat "$ISCSI_ROOT/$target/control/address" 2>/dev/null`; do
		local portal="$(echo "$addr" | cut -d '/' -f1):$TGTD_PORT";
		${SCSI_TGT}_show_target_portals target=$target ctl_port=$ctl_port | grep "$portal" 2>/dev/null
		[ $? -ne 0 ] && return 1
	done
	return 0
}

function pcs_iscsi_get_ctl_port {
	local target="$1"
	cat "$ISCSI_SHM/.running/$target" 2>/dev/null
	return $?
}

function pcs_iscsi_get_status {
	local target="$1"

	if [ ! -f "$ISCSI_SHM/.running/$target" ] ; then
		echo "stopped"
		return 0
	fi

	ctl_port=`cat "$ISCSI_SHM/.running/$target" 2>/dev/null`
	if [ -z "$ctl_port" ] ; then
		echo "service failed"
		return 1
	fi

	${SCSI_TGT}_get_target_status target=$target ctl_port=$ctl_port
	return $?
}

# start existing and registered target
function pcs_iscsi_up_target {
	local target="$1"
	local luns=""
	local ctl_port=""
	local addresses=""
	if [ ! -d  "$ISCSI_ROOT/$target" ] ; then
		echo "Target $target doesn't exist on $ISCSI_ROOT" 1>&2
		return 1
	fi

	pcs_iscsi_is_registered_local "$target"
	rc=$?
	if [ $rc -ne 0 ] ; then
		[ $rc -eq 1 ] && echo "Target $target not registered on current host" 1>&2
		return 2
	fi

	[ ! -d "$ISCSI_SHM/.running" ] && mkdir -p "$ISCSI_SHM/.running"

	if [ -f "$ISCSI_SHM/.running/$target" ]; then
		echo "Target already running." 1>&2
		return 3
	fi

	if [ -f "$ISCSI_ROOT/$target/control/address" ] ; then
		addresses=`cat "$ISCSI_ROOT/$target/control/address"`
		if [ -z "$addresses" ] ; then
			echo "Unknown address for target $target" 1>&2
			return 4
		fi
		for addr in $addresses; do
			${SCSI_TRANSPORT}_add_addr $addr
			[ $? -ne 0 ] && return 4
		done
	else
		echo "Unknown address for target $target" 1>&2
		return 5
	fi

	ctl_port=`get_id`

	${SCSI_TGT}_create_target target=$target addresses="$addresses" ctl_port=$ctl_port
	if [ $? -ne 0 ] ; then
		echo "Unable add target $target" 1>&2
		${SCSI_TRANSPORT}_remove_addr $addresses
		pcs_iscsi_down_target $target $ctl_port
		return 6
	fi

	# FIXME: Add IP wildcard to allow all initiators (this is insecure!).
	# FIXME: We must read allowed initiators from $ISCSI_ROOT/$target/control/allowed
	${SCSI_TGT}_disable_authentication target=$target ctl_port=$ctl_port
	if [ $? -ne 0 ]; then
		echo "Failed to bind allowed initiators" 1>&2
		pcs_iscsi_down_target $target $ctl_port
		return 7
	fi

	pcs_iscsi_bind_account $target $ctl_port
	if [ $? -ne 0 ] ; then
		echo "Failed to bind CHAP account to target" 1>&2
		pcs_iscsi_down_target $target $ctl_port
		return 8
	fi

	${SCSI_TGT}_configure_target target=$target ctl_port=$ctl_port
	if [ $? -ne 0 ]; then
		echo "Failed to configure target" 1>&2
		pcs_iscsi_down_target $target $ctl_port
		return 9
	fi

	luns=`ls -d "$ISCSI_ROOT/$target/"lun[0-9]* 2>/dev/null`
	if [ -n "$luns" ] ; then
		for l in $luns; do
			pcs_iscsi_up_lun $target $ctl_port `basename $l`
			if [ $? -ne 0 ] ; then
				pcs_iscsi_down_target $target $ctl_port
				return 10
			fi
		done
	fi

	echo $ctl_port | dd of="$ISCSI_ROOT/$target/control/.running" conv=fsync >/dev/null 2>&1
	if [ $? -ne 0 ] ; then
		echo "Can't store file $ISCSI_ROOT/$target/control/.running" 1>&2
		pcs_iscsi_down_target $target $ctl_port
		return 11
	fi

	echo $ctl_port > $ISCSI_SHM/.running/$target

	${SCSI_TGT}_enable_target target=$target ctl_port=$ctl_port
	if [ $? -ne 0 ] ; then
		echo "Can't enable target $target" 1>&2
		pcs_iscsi_down_target $target $ctl_port
		return 12
	fi

	return 0
}

# delete existing target, force flag allow delete target registered
# on another host
function pcs_iscsi_delete_target {
	local target="$1"
	local force="$2"

	if [ -z "$ISCSI_ROOT" ] ; then
		echo "ISCSI_ROOT must be specified" 1>&2
		return 1
	fi

	[ ! -d "$ISCSI_ROOT/$target" ] && return 0

	pcs_iscsi_is_registered_local "$target"
	rc=$?
	[ $rc -gt 1 ] && return $rc # pcs_iscsi_is_registered_local failed
	if [ $rc -eq 0 ]; then
		# target registered on current host
		if [ -f "$ISCSI_SHM/.running/$target" ] ; then
			echo "Can't delete running target" 1>&2
			return 2
		fi
		pcs_iscsi_unregister_target "$target"
	elif [ -z "$force" ]; then
		pcs_iscsi_target_has_owner $target
		rc=$?
		[ $rc -gt 1 ] && return $rc

		if [ $rc -eq 1 ] ; then
			echo "Target $target registered on another host" 1>&2
			return 3
		fi
	fi

	mv "$ISCSI_ROOT/$target" "$ISCSI_ROOT/tmp/$target"
	if [ $? -ne 0 ] ; then
		echo "Can't move directory $ISCSI_ROOT/$target to $ISCSI_ROOT/tmp/$target"
		return 4
	fi

	rm -rf "$ISCSI_ETC/targets/$target" $ISCSI_ROOT/tmp/$target
	return 0
}

function pcs_iscsi_set_chap_account {
	local target="$1"
	local user_name="$2"
	local dir="$3"

	[ "$user_name" == "-" ] && user_name=""

	if [ -n "$user_name" ]; then
		if [ -d "$ISCSI_ROOT/accounts/$user_name" ]; then
			rm -f "$dir/control/account"
			ln -sf "$ISCSI_ROOT/accounts/$user_name" "$dir/control/account"
			if [ $? -ne 0 ] ; then
				echo "Unable create link to account '$user_name'." 1>&2
				return 1
			fi
		else
			echo "Can't find CHAP account '$user_name'" 1>&2
			return 1
		fi
	elif [ -L "$dir/control/account" ]; then
		rm -f  "$dir/control/account"
	fi

	return 0
}

# create and register target
function pcs_iscsi_make_target {
	local target="$1"
	local addr="$2"
	local user_name="$3"
	local stor_attr=""

	if [ -z "$ISCSI_ROOT" ] ; then
		echo "ISCSI_ROOT must be specified" 1>&2
		return 1
	fi

	if [ -d "$ISCSI_ROOT/$target" ] ; then
		echo "Target $target already exist in $ISCSI_ROOT" 1>&2
		return 2
	fi

	tmp=`mktemp -d ${ISCSI_ROOT}/tmp/new-$target.XXXXXXXX`
	if [ $? -ne 0 ] ; then
		echo "Can't create temporary directory." 1>&2
		return 3
	fi

	is_root_on_vstorage || stor_attr="mds-storage=1"

	for dir in "$tmp/control" "$tmp/pr"; do
		mkdir -p "$dir" >/dev/null
		if [ $? -ne 0 ] ; then
			echo "Can't create directory $dir" 1>&2
			rm -rf $tmp
			return 4
		fi

		if [ -n "$stor_attr" ]; then
			pcs_iscsi_set_storage_attr "$dir" "$stor_attr"
			if [ $? -ne 0 ]; then
				rm -rf $tmp
				return 4
			fi
		fi
	done

	echo "$addr" | dd of="$tmp/control/address" conv=fsync >/dev/null 2>&1
	if [ $? -ne 0 ] ; then
		echo "Can't store address in file $tmp/control/address" 1>&2
		rm -rf $tmp
		return 5
	fi

	if [ ! -z "$user_name" ]; then
		pcs_iscsi_set_chap_account "$target" "$user_name" "$tmp"
		if [ $? -ne 0 ]; then
			rm -rf $tmp
			return 5
		fi
	fi

	mv "$tmp" "${ISCSI_ROOT}/$target" >/dev/null
	if [ $? -ne 0 ] ; then
		echo "Failed to create directory." 1>&2
		rm -rf $tmp
		return 6
	fi

	pcs_iscsi_register_target $target
	if [ $? -ne 0 ] ; then
		echo "Unable register target $target on this host" 1>&2
		return 7
	fi

	return 0
}

function pcs_iscsi_check_root {
	local create_dir="$1"
	if [ -z "$ISCSI_ROOT" ] ; then
		echo "Unable to find directory with iSCSI targets, please check 'ISCSI_ROOT'" 1>&2
		echo "in $ISCSI_ETC/config" 1>&2
		exit 1
	fi

	if [ ! -d "$ISCSI_ROOT" ]; then
		if [ "$create_dir" != "yes" ] ; then
			echo "Directory $ISCSI_ROOT does not exist" 1>&2
			exit 2
		fi
		mkdir -p $ISCSI_ROOT
		if [ $? -ne 0 ] ; then
			echo "Unable create directory $ISCSI_ROOT" 1>&2
			exit 3
		fi
	fi

	[ ! -d "$ISCSI_ROOT/tmp" ] && mkdir -p "$ISCSI_ROOT/tmp"
}

function pcs_iscsi_check_target {
	local target="$1"
	if [ ! -d  "$ISCSI_ROOT/$target" ] ; then
		echo "Target $target doesn't exist on $ISCSI_ROOT" 1>&2
		exit 1
	fi

	pcs_iscsi_is_registered_local "$target"
	rc=$?
	if [ $rc -ne 0 ] ; then
		[ $rc -eq 1 ] && echo "Target $target not registered on current host" 1>&2
		exit 2
	fi
}

function pcs_iscsi_target_can_stop_nonforcibly {
	local ctl_port="$1"; shift
	local target="$1"

	if [ -z "$ctl_port" ]; then
		echo "Control port is unknown" 1>&2
		return 1
	fi

	local initiators=$(${SCSI_TGT}_show_initiators target=$target ctl_port=$ctl_port)
	if [ -n "$initiators" ]; then
		echo "initiators still connected" 1>&2
		echo "$initiators" 1>&2
		return 2
	fi
	return 0
}

function pcs_iscsi_lock_exec {
	local cmd="$1" ; shift
	local target="$1"

	[ ! -d $ISCSI_SHM ] && mkdir -p $ISCSI_SHM
	(
		flock -w 30 -x 222
		if [ $? -ne 0 ] ; then
			echo "Unable lock target $target" 1>&2
			return 10;
		fi
		# '222<&-' explicitely closes fd 222 for childs, especially tgtd
		(${cmd} "${@}") 222<&-
		return $?
	) 222>"$ISCSI_SHM/.lock_$target"
	return $?
}

function pcs_iscsi_save_snapshot_info {
	local target="$1"
	local lun_id="$2"
	local uuid="$3"
	local descr="$4"
	mkdir -p "$ISCSI_ROOT/snapshots" >/dev/null 2>&1
	echo "SNAP_CREATE_DATE='$(date +"%F %T")'; SNAP_DESCRIPTION='${descr}'; SNAP_IQN='$target'; SNAP_LUN='${lun_id}'" \
		| dd of="$ISCSI_ROOT/snapshots/$uuid" conv=fsync >/dev/null 2>&1
	if [ $? -ne 0 ] ; then
		echo "Unable save description for new snapshot" 1>&2
		return 3
	fi
	return 0
}

function pcs_iscsi_delete_snapshot_info {
	local lun_dir="$1"
	$PLOOP snapshot-list -H -o UUID "$lun_dir/DiskDescriptor.xml" | \
		while read snap_uuid; do
			snap_uuid=`expr "$snap_uuid" : "^\{\(.*\)\}$"`
			rm -f "$ISCSI_ROOT/snapshots/$snap_uuid"
		done
}

function pcs_iscsi_get_root() {
	local root="$1"
	if [ -n "$root" ] ; then
		echo "$root"
		return
	fi

	root="$ISCSI_ROOT"
	if [ -z "$root" ] ; then
		# ask shaman for ISCSI root
		cluster=`/usr/sbin/shaman stat 2>/dev/null | grep Cluster | tr "'" " " | awk '{ print $2 }'`
		[ -z "$cluster" ] && return
		mnt=`grep "[pv]storage://$cluster" /proc/mounts | awk '{ print $2 }'`
		[ -z "$mnt" ] && return
		root="$mnt/iscsi"
	fi
	echo "$root"
}

function pcs_iscsi_set_limit {
	local target="$1"
	local type="$2"
	local limit="$3"
	local ctl_port=`pcs_iscsi_get_ctl_port $target`
	local limits_file="$ISCSI_ROOT/$target/control/limits"

	if [ $(pcs_iscsi_get_status "$target") == "running" ]; then
		${SCSI_TGT}_set_limit target=$target ctl_port=$ctl_port type=$type limit=$limit
		if [ $? -ne 0 ]; then
			echo "Unable to set limit=$limit" 1>&2
			return 1
		fi
	fi

	grep -q "$type" "$limits_file" 2>/dev/null && sed -i "s/$type=[0-9]*/$type=$limit/g" "$limits_file" || echo "$type=$limit" >> "$limits_file"
}

function pcs_iscsi_set_limit_from_file {
	local target="$1"
	local ctl_port=`pcs_iscsi_get_ctl_port $target`
	local limits_file="$ISCSI_ROOT/$target/control/limits"

	if [ $(pcs_iscsi_get_status "$target") != "running" ]; then
		echo "Target $target is not running" 1>&2
		return 1
	fi

	local kbps_limit=$(pcs_iscsi_get_limit "$target" "max_kbps")
	local iops_limit=$(pcs_iscsi_get_limit "$target" "max_iops")

	pcs_iscsi_set_limit "$target" "max_kbps" "$kbps_limit"
	pcs_iscsi_set_limit "$target" "max_iops" "$iops_limit"
}

function pcs_iscsi_get_limit {
	local target="$1"
	local type="$2"
	local limits_file="$ISCSI_ROOT/$target/control/limits"

	if [ ! -f $limits_file ]; then
		echo 0
		return 0
	fi

	local limit=$(grep -Po "(?<=$type=).*" "$limits_file" 2>&1)
	if [ -z "$limit" ]; then
		echo "Failed to read file \"$limits_file\"" 1>&2
		echo 0
		return 1
	fi
	echo "$limit"
}