#!/usr/local/bin/cbsd
#v11.2.1
MYARG="mode"
MYOPTARG="name path display type jname"
MYDESC="Operate with virtual storage media such as ISO"
CBSDMODULE="sys"
EXTHELP="wf_bhyve.html"
ADDHELP="mode=attach - attach media to jname\n\
mode=detach - detach media from jname\n\
mode=list - list of registered media\n\
mode=flushall - unregister all records, without deleting\n\
mode=register - register new media (req: name, path)\n\
mode=unregister - unregister new media (req: name, path), without delete\n\
mode=delete|remove - unregister and delete media file (req: name, path)\n\
mode=deleteall|removeall - unregister and delete all ISO files\n\
mode=get - print 'name' or 'path' for 'path=' or 'name='\n\
mode=update - update type or jname by 'path=' and 'name='\n\
mode=dump - dump SQL records by line\n\
mode=eject - eject CD/ISO from VM\n\
type=iso,hdd or shared\n\
display= list by comma for column. Default: name,path,type,jname,size\n"

. ${subr}
. ${system}
. ${strings}
. ${tools}

init $*

. ${workdir}/bhyve.subr
. ${workdir}/xen.subr
. ${workdir}/virtual.subr

[ -z "${cbsd_queue_name}" ] && cbsd_queue_name="/clonos/media/"
[ -z "${display}" ] && display="name,path,type,jname,size"

#remove commas for loop action on header
mydisplay=$( echo ${display} | /usr/bin/tr ',' ' ' )

# upper for header
myheader=$( echo ${mydisplay} | /usr/bin/tr '[:lower:]' '[:upper:]' )

show_header()
{
	local _header="${WHITE}${BOLD}${myheader}${NORMAL}"
	[ ${header} -eq 1 ] && ${ECHO} "${_header}"
}

# 1) if -n name -p path -t type exist for jail='-' than UPDATE this records to -j jname
# 2) if -n name -p path -t type not exist for jail='-', remove records for -j jname
unregister_or_remove()
{
	local rec_num
	local exist
	local name path type jname

	while getopts "n:p:t:j:" opt; do
		case "$opt" in
			n) name="${OPTARG}" ;;
			p) path="${OPTARG}" ;;
			t) type="${OPTARG}" ;;
			j) jname="${OPTARG}" ;;
		esac
		shift $(($OPTIND - 1))
	done

	if [ -z "${jname}" -o "${jname}" = "-" ]; then
		cbsdsql storage_media "DELETE FROM media WHERE jname=\"${jname}\" AND name=\"${name}\" AND path=\"${path}\" AND type=\"${type}\""
		return 0
	fi

	rec_num=$( cbsdsql storage_media SELECT COUNT\(path\) FROM media WHERE name=\"${name}\" AND path=\"${path}\" AND type=\"${type}\" AND jname != \"${jname}\" )

	if [ "${rec_num}" = "0" ]; then
		# this is last one, clean ISO register and drop to unassigned stage
		# echo "cbsdsql storage_media UPDATE media SET jname='-' WHERE jname=\"${jname}\" AND type=\"${type}\""
		cbsdsql storage_media "UPDATE media SET jname='-' WHERE jname=\"${jname}\" AND type=\"${type}\" AND name=\"${name}\" AND path=\"${path}\""

	else
		# delete iso registered when we have greater then 1 records with path= name=
		# echo "cbsdsql storage_media DELETE FROM media WHERE jname=\"${jname}\" AND name=\"${name}\" AND path=\"${path}\" AND type=\"${type}\""
		cbsdsql storage_media "DELETE FROM media WHERE jname=\"${jname}\" AND name=\"${name}\" AND path=\"${path}\" AND type=\"${type}\""
	fi

	# delete from domain local db
	cbsdsql ${jailsysdir}/${jname}/local.sqlite "DELETE FROM ${emulator}dsk WHERE dsk_path=\"hdd-${name}\" AND dsk_type=\"${type}\""

	# CBSD QUEUE
	if [ -x "${moduledir}/cbsd_queue.d/cbsd_queue" ]; then
		[ "${cbsd_queue_name}" != "none" ] && cbsd_queue cbsd_queue_name=${cbsd_queue_name} cmd=refresh
	fi
}

# 1) if -n name -p path -t type exist for jail='-' than UPDATE this records to -j jname
# 2) if -n name -p path -t type not exist for jail='-', add records for -j jname
register_or_add()
{
	local rec_num
	local exist
	local name path type jname
	local size

	while getopts "n:p:t:j:" opt; do
		case "${opt}" in
			n) name="${OPTARG}" ;;
			p) path="${OPTARG}" ;;
			t) type="${OPTARG}" ;;
			j) jname="${OPTARG}" ;;
		esac
		shift $(($OPTIND - 1))
	done

	populate_dsk_size ${path}
	size=${dsk_bsize}
	[ -z "${size}" ] && size=0

	if [ -z "${jname}" -o "${jname}" = "-" ]; then
		rec_num=$( cbsdsql storage_media SELECT COUNT\(path\) FROM media WHERE name=\"${name}\" AND path=\"${path}\" AND type=\"${type}\" AND jname=\"-\" )
		[ "${rec_num}" != "0" ] && return 0
		cbsdsql storage_media "INSERT INTO media ( name, path, type, jname, size ) VALUES ( \"${name}\", \"${path}\", \"${type}\", \"${jname}\", \"${size}\" )"
		return 0
	fi

	rec_num=$( cbsdsql storage_media SELECT COUNT\(path\) FROM media WHERE name=\"${name}\" AND path=\"${path}\" AND type=\"${type}\" AND jname=\"-\" )

	if [ "${rec_num}" = "1" ]; then
		# echo "cbsdsql storage_media UPDATE media SET jname=\"${jname}\" WHERE jname=\"${jname}\" AND type=\"${type}\""
		cbsdsql storage_media "UPDATE media SET jname=\"${jname}\" WHERE jname=\"-\" AND name=\"${name}\" AND path=\"${path}\" AND type=\"${type}\""
	else
		# echo "cbsdsql storage_media INSERT INTO media ( name, path, type, jname ) VALUES ( \"${name}\", \"${path}\", \"${type}\", \"${jname}\" )"
		cbsdsql storage_media "INSERT INTO media ( name, path, type, jname, size ) VALUES ( \"${name}\", \"${path}\", \"${type}\", \"${jname}\", \"${size}\" )"
	fi

	# add to domain local db
	cbsdsql ${jailsysdir}/${jname}/local.sqlite "DELETE FROM ${emulator}dsk WHERE dsk_path=\"hdd-${name}\" AND dsk_type=\"${type}\""

	# CBSD QUEUE
	if [ -x "${moduledir}/cbsd_queue.d/cbsd_queue" ]; then
		[ "${cbsd_queue_name}" != "none" ] && cbsd_queue cbsd_queue_name=${cbsd_queue_name} cmd=refresh
	fi
}

# if $1 = "Unregister" then overwrite status to "Unregister"
populate_output_data()
{
	local _i _val

	_status=

	#populate values for in output string
	for _i in ${mydisplay}; do
		case "${_i}" in
			size)
				_val=
				eval _val=\$$_i

				if conv2human "${_val}"; then
					_val=${convval}
				fi
				;;
			*)
				_val=
				eval _val=\$$_i
				[ -z "${_val}" ] && _val="\-"
				prefix=$( substr --pos=0 --len=1 --str=${_val} )
				[ "${prefix}" = "-" ] && _val="\-"
				;;
		esac
		if [ -z "${_status}" ]; then
			_status="${NORMAL}${_val}"
		else
			_status="${_status} ${_val}"
		fi
	done
}


# $1 - which file from. Eg: local
show_data_from_sql()
{
	local _i

	_sql="SELECT name,path,type,jname,size FROM media"

	cbsdsql storage_media ${_sql} | while read name path type jname size; do
		populate_output_data
		printf "${GREEN}"
		printf "${_status}"
		printf "${NORMAL}\n"
	done

	IFS=" "
}


show_local()
{
	local _errcode _status

	show_header
	show_data_from_sql local
}

show_vhid()
{
	show_local
}


# select into $vm_res variable path of media by name
# return 0 if data exist
# $1 - alternative jname
select_by_name()
{
	local j="${jname}"

	[ -n "${1}" ] && j="${1}"

	vm_res=$( cbsdsql storage_media SELECT path FROM media WHERE name=\"${name}\" AND jname=\"${j}\" )

	[ -z "${vm_res}" ] && return 1

	return 0
}

# select into $vm_res variable name of media by path
# return 0 if data exist
# $1 - alternative jname
select_by_path()
{
	local j="${jname}"

	[ -n "${1}" ] && j="${1}"
	vm_res=$( cbsdsql storage_media SELECT name FROM media WHERE path=\"${path}\" AND jname=\"${j}\" )

	[ -z "${vm_res}" ] && return 1

	return 0
}

update_jname()
{
	cbsdsql storage_media "UPDATE media SET jname=\"${jname}\" WHERE name=\"${name}\" AND path=\"${path}\""
}

update_type()
{
	cbsdsql storage_media "UPDATE media SET type=\"${type}\" WHERE name=\"${name}\" AND path=\"${path}\" AND jname=\"${jname}\""
}

check_protected()
{
	local _emulator
	local _table

	[ "${jname}" = "-" ] && return 0

	_emulator=$( cbsdsql local SELECT emulator FROM jails WHERE jname=\"${jname}\" )

	case "${_emulator}" in
		jail)
			_table="jails"
			;;
		bhyve)
			_table="bhyve"
			;;
		xen)
			_table="xen"
			;;
	esac

	protected=$( cbsdsql local SELECT protected FROM ${_table} WHERE jname=\"${jname}\" )

	[ -z "${protected}" ] && protected="0"	# by default - is not protected
	[ "${protected}" = "1" ] && err 1 "${MAGENTA}Environment is protected to delete: ${GREEN}${jname}${NORMAL}"
}


storage_attach()
{
	[ -z "${name}" ] && err 1 "${MAGENTA}media: ${GREEN}name=${NORMAL}"
	[ -z "${path}" ] && err 1 "${MAGENTA}media: ${GREEN}path=${NORMAL}"
	[ -z "${jname}" ] && err 1 "${MAGENTA}Give me ${GREEN}jname=${NORMAL}"

	local attached_to_name dsk_path dsk_name
	local mydb virtio_type already_attached_to_me

	attached_to_jname=$( cbsdsql storage_media SELECT jname FROM media WHERE path=\"${path}\" AND name=\"${name}\" AND jname!=\"${jname}\" LIMIT 1 )

	# not attached?
	if [ "${attached_to_jname}" = "-" ]; then
		by_path="-"
		by_name="-"
		attached_to_jname=
	else
		by_path="${path}"
		by_name="${name}"
	fi

	# todo: shared disk
	if [ "${type}" = "hdd" ]; then
		[ -n "${attached_to_jname}" ] && err 1 "${MAGENTA}disk ${name} with path ${path} already attached to: ${GREEN}${test_jname}${NORMAL}"
		select_by_path "${by_path}"
		[ $? -ne 0 ] && err 1 "${MAGENTA}Path not exist for jname by path ${by_path}: ${GREEN}${path}${NORMAL}"
		select_by_name "${by_name}"
		[ $? -ne 0 ] && err 1 "${MAGENTA}Name not exist for jname by name ${by_name}: ${GREEN}${name}${NORMAL}"
	fi

	# save variables before rcconf init
	dsk_path="${path}"
	dsk_name="${name}"

	. ${jrcconf}
	[ $? -eq 1 ] && err 1 "${MAGENTA}attachzfs: no such jail: ${GREEN}${jname}${NORMAL}"

	case "${type}" in
		iso|shared)
			;;
		hdd)
			mydb="${jailsysdir}/${jname}/local.sqlite"
			virtio_type="virtio-blk"
			cbsdsql ${mydb} "INSERT INTO ${emulator}dsk ( jname,dsk_controller,dsk_path,dsk_slot ) VALUES ( \"${jname}\",\"${virtio_type}\",\"${dsk_path}\","0" )"
			;;
	esac

	# test for already attached
	already_attached_to_me=$( cbsdsql storage_media SELECT jname FROM media WHERE path=\"${dsk_path}\" AND name=\"${dsk_name}\" AND jname=\"${jname}\" LIMIT 1 )

	[ -n "${already_attached_to_me}" ] && err 1 "${MAGENTA}storage with name:${name} and path:${path} already attached for instance: ${GREEN}${jname}${NORMAL}"

	if [ -n "${attached_to_jname}" ]; then
		#shared disk: INSERT
		sql="INSERT INTO media ( name, path, type, jname ) VALUES ( \"${dsk_name}\", \"${dsk_path}\", \"${type}\", \"${jname}\" )"
	else
		sql="UPDATE media SET jname=\"${jname}\" WHERE name=\"${dsk_name}\" AND path=\"${dsk_path}\""
	fi

	echo "${sql}"
	cbsdsql ${dbdir}/storage_media.sqlite "${sql}"

	# CBSD QUEUE
	if [ -x "${moduledir}/cbsd_queue.d/cbsd_queue" ]; then
		[ "${cbsd_queue_name}" != "none" ] && cbsd_queue cbsd_queue_name=${cbsd_queue_name} cmd=refresh
	fi

	return 0
}


storage_detach()
{
	[ -z "${name}" ] && err 1 "${MAGENTA}media: ${GREEN}name=${NORMAL}"
	[ -z "${path}" ] && err 1 "${MAGENTA}media: ${GREEN}path=${NORMAL}"
	[ -z "${jname}" ] && err 1 "${MAGENTA}Give me ${GREEN}jname=${NORMAL}"

	local dsk_path dsk_name
	local mydb virtio_type already_attached_to_me

	attached_to_me=$( cbsdsql storage_media SELECT jname FROM media WHERE path=\"${path}\" AND name=\"${name}\" AND jname=\"${jname}\" LIMIT 1 )
	[ "${attached_to_me}" != "${jname}" ] && err 1 "${MAGENTA}disk ${name} with name:${name} and path:${path} is not attached to: ${GREEN}${jname}${NORMAL}"

	attached_to_jname=$( cbsdsql storage_media SELECT jname FROM media WHERE path=\"${path}\" AND name=\"${name}\" AND jname!=\"${jname}\" LIMIT 1 )

	# not attached?
	if [ "${attached_to_jname}" = "-" ]; then
		by_path="-"
		by_name="-"
		attached_to_jname=
	else
		by_path="${path}"
		by_name="${name}"
	fi

	# save variables before rcconf init
	dsk_path="${path}"
	dsk_name="${name}"

	. ${jrcconf}
	[ $? -eq 1 ] && err 1 "${MAGENTA}attachzfs: no such jail: ${GREEN}${jname}${NORMAL}"

	case "${type}" in
		iso|shared)
			;;
		hdd)
			mydb="${jailsysdir}/${jname}/local.sqlite"
			cbsdsql ${mydb} "DELETE FROM ${emulator}dsk WHERE dsk_path=\"${dsk_path}\" AND name=\"${dsk_name}\""
			;;
	esac

	if [ -n "${attached_to_jname}" ]; then
		# shared disk: delete record with my jname
		sql="DELETE FROM media WHERE name=\"${dsk_name}\" AND path=\"${dsk_path}\" AND jname=\"${jname}\""
	else
		# disk is orphaned now: drop to '-' jname
		sql="UPDATE media SET jname='-' WHERE jname=\"${jname}\" AND name=\"${dsk_name}\" AND path=\"${dsk_path}\""
	fi

	echo "${sql}"
	cbsdsql ${dbdir}/storage_media.sqlite "${sql}"

	# CBSD QUEUE
	if [ -x "${moduledir}/cbsd_queue.d/cbsd_queue" ]; then
		[ "${cbsd_queue_name}" != "none" ] && cbsd_queue cbsd_queue_name=${cbsd_queue_name} cmd=refresh
	fi

	case "${type}" in
		hdd)
			mydb="${jailsysdir}/${jname}/local.sqlite"
			cbsdsql ${mydb} "DELETE FROM ${emulator}dsk WHERE dsk_path=\"${dsk_path}\""
			;;
		iso)
			bset mode=quiet jname=${jname} vm_iso_path=''
			;;
	esac

	return 0
}


eject()
{
	cbsdsql storage_media "UPDATE media SET jname='-' WHERE jname=\"${jname}\" AND type=\"iso\""
}


#### MAIN
[ -z "${header}" ] && header=1
sqldelimer=" "
vm_res=

case "${mode}" in
	attach)
		storage_attach
		err 0 "${MAGENTA}Attached to: ${GREEN}${jname}${NORMAL}"
		;;
	detach)
		storage_detach
		err 0 "${MAGENTA}Detach to: ${GREEN}${jname}${NORMAL}"
		;;
	eject)
		[ -z "${jname}" ] && err 1 "${MAGENTA}Please specify ${GREEN}jname=${NORMAL}"
		eject
		err 0 "${MAGENTA}All CD/ISO ejected: ${GREEN}${jname}${NORMAL}"
		;;
	list)
		show_local | /usr/bin/column -t
		;;
	get)
		[ -z "${name}" -a -z "${path}" ] && err 1 "${MAGENTA}media: ${GREEN}name=${MAGENTA} or ${GREEN}path=${MAGENTA} value is reguired${NORMAL}"
		[ -n "${name}" -a -n "${path}" ] && err 1 "${MAGENTA}media: Please specify ${GREEN}name=${MAGENTA} OR ${GREEN}path=${MAGENTA}, not both${NORMAL}"
		[ -n "${name}" ] && select_by_name
		[ -n "${path}" ] && select_by_path
		err 0 "${vm_res}"
		;;
	register)
		[ -z "${name}" ] && err 1 "${MAGENTA}media: ${GREEN}name=${MAGENTA} is mandatory${NORMAL}"
		[ -z "${path}" ] && err 1 "${MAGENTA}media: ${GREEN}path=${MAGENTA} is mandatory${NORMAL}"
		select_by_path && err 1 "${MAGENTA}Path already exist for: ${GREEN}${vm_res}${NORMAL}"
		select_by_name && err 1 "${MAGENTA}Name already exist for: ${GREEN}${vm_res}${NORMAL}"
		[ -z "${type}" ] && type="hdd"
		[ -z "${jname}" ] && jname="-"

		register_or_add -n "${name}" -p "${path}" -t "${type}" -j "${jname}"

		# CBSD QUEUE
		if [ -x "${moduledir}/cbsd_queue.d/cbsd_queue" ]; then
			[ "${cbsd_queue_name}" != "none" ] && cbsd_queue cbsd_queue_name=${cbsd_queue_name} cmd=refresh
		fi

		err 0 "${MAGENTA}Updated${NORMAL}"
		;;
	unregister)
		[ -z "${type}" ] && type="hdd"
		[ -z "${jname}" ] && jname="-"

		check_protected

		[ -z "${name}" -a -z "${path}" ] && err 1 "${MAGENTA}media: ${GREEN}name=${MAGENTA} or ${GREEN}path=${MAGENTA} value is reguired${NORMAL}"

		if [ -n "${name}" -a -n "${path}" ]; then
			unregister_or_remove -n "${name}" -p "${path}" -t "${type}" -j "${jname}"
		elif [ -n "${name}" ]; then
			cbsdsql storage_media "DELETE FROM media WHERE name=\"${name}\" AND jname=\"${jname}\""
		elif [ -n "${path}" ]; then
			cbsdsql storage_media "DELETE FROM media WHERE path=\"${path}\" AND jname=\"${jname}\""
		fi

		# CBSD QUEUE
		if [ -x "${moduledir}/cbsd_queue.d/cbsd_queue" ]; then
			[ "${cbsd_queue_name}" != "none" ] && cbsd_queue cbsd_queue_name=${cbsd_queue_name} cmd=refresh
		fi

		err 0 "${MAGENTA}Unregistered${NORMAL}"
		;;
	flushall)
		# test for protected by select jname ???
		cbsdsql storage_media "DELETE FROM media WHERE type != \"hdd\""

		# CBSD QUEUE
		if [ -x "${moduledir}/cbsd_queue.d/cbsd_queue" ]; then
			[ "${cbsd_queue_name}" != "none" ] && cbsd_queue cbsd_queue_name=${cbsd_queue_name} cmd=refresh
		fi

		err 0 "${MAGENTA}Flushed${NORMAL}"
		;;
	delete|remove)
		[ -z "${type}" ] && type="hdd"
		[ -z "${jname}" ] && jname="-"

		check_protected

		[ -z "${name}" -a -z "${path}" ] && err 1 "${MAGENTA}media: ${GREEN}name=${MAGENTA} or ${GREEN}path=${MAGENTA} value is reguired${NORMAL}"

		if [ -n "${name}" -a -n "${path}" ]; then
			if [ "${jname}" = "-" ]; then
				cbsdsql storage_media "DELETE FROM media WHERE name=\"${name}\" AND path=\"${path}\" AND type=\"${type}\""
			else
				cbsdsql storage_media "DELETE FROM media WHERE name=\"${name}\" AND path=\"${path}\" AND type=\"${type}\" AND jname=\"${jname}\""
			fi
		else
			if [ -n "${name}" ]; then
				select_by_name
				path="${vm_res}"
				cbsdsql storage_media "DELETE FROM media WHERE name=\"${name}\" AND jname=\"${jname}\""
			elif [ -n "${path}" ]; then
				cbsdsql storage_media "DELETE FROM media WHERE path=\"${path}\" AND jname=\"${jname}\""
			fi
		fi

		[ -z "${path}" ] && err 1 "${MAGENTA}media delete: empty path variable${NORMAL}"

		short_dsk_path=$( /usr/bin/basename ${path} )

		case "${type}" in
			iso)
				/bin/rm -f ${path}
				;;
			vhd|hdd)
				mydb="${jailsysdir}/${jname}/local.sqlite"
				#echo "cbsdsql ${mydb} \"DELETE FROM ${emulator}dsk WHERE dsk_path=\"${short_dsk_path}\"\""
				cbsdsql ${mydb} "DELETE FROM ${emulator}dsk WHERE dsk_path=\"${short_dsk_path}\""
				${emulator}_remove_dsk ${short_dsk_path}
				;;
		esac

		# CBSD QUEUE
		if [ -x "${moduledir}/cbsd_queue.d/cbsd_queue" ]; then
			[ "${cbsd_queue_name}" != "none" ] && cbsd_queue cbsd_queue_name=${cbsd_queue_name} cmd=refresh
		fi

		err 0 "${MAGENTA}Deleted${NORMAL}"
		;;
	deleteall|removeall)
		for i in $( cbsdsql storage_media SELECT path FROM media WHERE type != \"hdd\" ); do
			cbsdsql storage_media "DELETE FROM media WHERE path=\"${i}\""
			[ -r "${i}" ] && /bin/rm -f ${i}
		done

		# CBSD QUEUE
		if [ -x "${moduledir}/cbsd_queue.d/cbsd_queue" ]; then
			[ "${cbsd_queue_name}" != "none" ] && cbsd_queue cbsd_queue_name=${cbsd_queue_name} cmd=refresh
		fi

		err 0 "${MAGENTA}Deleted${NORMAL}"
		;;
	update)
		[ -n "${type}" ] && update_type
		[ -n "${jname}" ] && update_jname
		;;
	dump)
		# export media data to ascii file
		[ ! -r ${dbdir}/storage_media.sqlite ] && err 1 "${MAGENTA}Not readable: ${GREEN}${dbdir}/storage_media.sqlite${NORMAL}"
		if [ -n "${jname}" ]; then
			_sql="SELECT name,path,type,size,jname FROM media WHERE jname=\"${jname}\""
		else
			_sql="SELECT name,path,type,size,jname FROM media"
		fi
		osqldelimer=${sqldelimer}
		sqldelimer="|"
		OIFS=${IFS}
		IFS="|"
		cbsdsql storage_media ${_sql} | while read name path type size jname; do
			_mysql="INSERT INTO media ( name, path, type, size, jname ) VALUES ( \"${name}\", \"${path}\", \"${type}\", \"${size}\", \"${jname}\" );"
			echo "${_mysql}"
		done
		IFS=${OIFS}
		sqldelimer=${osqldelimer}
		;;
	*)

		err 1 "${MAGENTA}Unknown mode: ${GREEN}${mode}${NORMAL}"
		;;
esac

