#!/sbin/openrc-run

description="Virtual Machine Management (libvirt) Guests"

depend() {
	use libvirtd
}

# set the default to QEMU
[ -z "${LIBVIRT_URIS}" ] && LIBVIRT_URIS="qemu:///system"

# default to suspending the VM via managedsave
case "${LIBVIRT_SHUTDOWN}" in
	managedsave|shutdown|destroy) ;;
	*) LIBVIRT_SHUTDOWN="managedsave" ;;
esac

# default to 500 seconds
[ -z ${LIBVIRT_MAXWAIT} ] && LIBVIRT_MAXWAIT=500

gueststatefile="/var/lib/libvirt/libvirt-guests.state"
netstatefile="/var/lib/libvirt/libvirt-net.state"

do_virsh() {
	local hvuri=$1
	shift

	# if unset, default to qemu
	[ -z ${hvuri} ] && hvuri="qemu:///system"
	# if only qemu was supplied then correct the value
	[ "xqemu" = x${hvuri} ] && hvuri="qemu:///system"

	# Silence errors because virsh always throws an error about
	# not finding the hypervisor version when connecting to libvirtd
	# lastly strip the blank line at the end
	LC_ALL=C virsh -c ${hvuri} "$@" 2>/dev/null | head -n -1
}

libvirtd_dom_list() {
	# Only work with domains by their UUIDs
	local hvuri=$1
	shift

	# The grep is to remove dom0 for xen domains. Otherwise we never hit 0
	do_virsh "${hvuri}" list --uuid $@ | grep -v 00000000-0000-0000-0000-000000000000
}

libvirtd_dom_count() {
	local hvuri=$1
	shift

	libvirtd_dom_list "${hvuri}" $@ | wc -l
}

libvirtd_net_list() {
	# Only work with networks by their UUIDs
	local hvuri=$1
	shift

	do_virsh "${hvuri}" net-list --uuid $@
}

libvirtd_net_count() {
	local hvuri=$1
	shift

	libvirtd_net_list "${hvuri}" $@ | wc -l
}

libvirtd_dom_stop() {
	# stops all persistent or transient domains for a given URI
	# $1 - uri
	# $2 - persisent/transient

	local uri=$1
	local persist=$2
	local shutdown_type=${LIBVIRT_SHUTDOWN}
	local counter=${LIBVIRT_MAXWAIT}
	local dom_name=
	local dom_as=
	local dom_ids=
	local uuid=
	local dom_count=

	[ "${persist}" = "--transient" ] && shutdown_type="shutdown"
	[ -n "${counter}" ] || counter=500

	einfo " Shutting down domain(s) ..."

	# grab all persistent or transient domains running
	dom_ids=$(libvirtd_dom_list ${uri} ${persist})

	for uuid in ${dom_ids}; do
		# Get the name
		dom_name=$(do_virsh ${uri} domname ${uuid})
		einfo "  ${dom_name}"
		# Get autostart state
		dom_as=$(do_virsh ${uri} dominfo ${uuid} | \
			awk '$1 == "Autostart:" { print $2 }')

		if [ "${persist}" = "--persistent" ]; then
			# Save our running state only if LIBVIRT_IGNORE_AUTOSTART != yes
			if  [ "x${LIBVIRT_IGNORE_AUTOSTART}" = "xyes" ] && \
				[ ${dom_as} = "enabled" ]; then
				:
			else
				echo "${uri} ${uuid}" >> ${gueststatefile}
			fi

		fi

		# Now let's stop it
		do_virsh "${uri}" ${shutdown_type} ${uuid} > /dev/null

	done

	dom_count="$(libvirtd_dom_count ${uri} ${persist})"
	while [ ${dom_count} -gt 0 ] && [ ${counter} -gt 0 ] ; do
		dom_count="$(libvirtd_dom_count ${uri} ${persist})"
		sleep 1
		if [ "${shutdown_type}" = "shutdown" ]; then
			counter=$((${counter} - 1))
		fi
		printf "."
	done

	if [ "${shutdown_type}" = "shutdown" ]; then
		# grab all domains still running
		dom_ids=$(libvirtd_dom_list ${uri} ${persist})
		for uuid in ${dom_ids}; do
			dom_name=$(do_virsh ${uri} domname ${uuid})
			eerror "  ${dom_name} forcibly stopped"
			do_virsh "${uri}" destroy ${uuid} > /dev/null
		done
	fi
}

libvirtd_net_stop() {
	# stops all persistent or transient domains for a given URI
	# $1 - uri
	# $2 - persisent/transient

	local uri=$1
	local persist=$2
	local uuid=
	local net_name=

	if [ "${LIBVIRT_NET_SHUTDOWN}" != "no" ]; then

		einfo " Shutting down network(s):"
		for uuid in $(libvirtd_net_list ${uri} ${persist}); do
			net_name=$(do_virsh ${uri} net-name ${uuid})
			einfo "   ${net_name}"

			if [ "${persist}" = "--persistent" ]; then
				# Save our running state
				echo "${uri} ${uuid}" >> ${netstatefile}

			fi

			# Actually stop the network
			do_virsh qemu net-destroy ${uuid} > /dev/null
		done

	fi
}

start() {
	local uri=
	local uuid=
	local name=

	for uri in ${LIBVIRT_URIS}; do
		do_virsh "${uri}" connect
		if [ $? -ne 0 ]; then
			eerror "Failed to connect to '${uri}'. Domains may not start."
		fi
	done

	[ ! -e "${netstatefile}" ] && touch "${netstatefile}"
	[ ! -e "${gueststatefile}" ] && touch "${gueststatefile}"

	# if the user didn't want to start any guests up then respect their wish
	[ "x${LIBVIRT_START}" = "xno" ] && return 0

	# start networks
	ebegin "Starting libvirt networks"
	while read -r uri uuid
	do
		# ignore trash
		[ -z "${uri}" ] || [ -z "${uuid}" ] && continue

		name=$(do_virsh "${uri}" net-name ${uuid})
		einfo "  ${name}"
		do_virsh "${uri}" net-start ${uuid} > /dev/null
	done <"${netstatefile}"
	eend 0

	# start domains
	ebegin "Starting libvirt domains"
	while read -r uri uuid
	do
		# ignore trash
		[ -z "${uri}" ] || [ -z "${uuid}" ] && continue

		name=$(do_virsh "${uri}" domname ${uuid})
		einfo "  ${name}"
		do_virsh "${uri}" start ${uuid} > /dev/null
        do_virsh "${uri}" domtime --sync ${uuid} > /dev/null
	done <"${gueststatefile}"
	eend 0
}

stop() {
	local counter=
	local dom_name=
	local net_name=
	local dom_ids=
	local uuid=
	local dom_count=

	rm -f "${gueststatefile}"
	[ $? -ne 0 ] && eerror "Unable to save domain state"
	rm -f "${netstatefile}"
	[ $? -ne 0 ] && eerror "Unable to save net state"

	for uri in ${LIBVIRT_URIS}; do
		einfo "Stopping libvirt domains and networks for ${uri}"

		libvirtd_dom_stop "${uri}" "--persistent"
		libvirtd_dom_stop "${uri}" "--transient"
		libvirtd_net_stop "${uri}" "--persistent"
		libvirtd_net_stop "${uri}" "--transient"

		einfo "Done stopping domains and networks for ${uri}"
	done
}