#!/usr/bin/bash

if [ $# -lt 1 ]; then
	echo $0 '<file.img> [qemu arguments]'
	exit 1
fi

if ! which qemu-kvm >/dev/null 2>&1; then
	echo qemu is not installed
	exit 1
fi

if [ -z "$TMPDIR" ]; then
	TMPDIR=/tmp
fi

WANT_SPICE=1
SPICE_REMOTE=0
SCREEN=0

while :; do
	case "$1" in
		--nospice)
			WANT_SPICE=0
			;;
		--spiceremote)
			SPICE_REMOTE=1
			;;
		--screen)
			SCREEN=1
			;;
		*)
			break
			;;
	esac
	shift
done

FILE=$1
shift
WIN10=0
WINOLD=0
NBSD=0
FACT=0
EFI=0
BIT32=0
RAW=""
OVMF_CODE=/usr/share/qemu/ovmf-x86_64-opensuse-code.bin
OVMF_VARS_SRC=/usr/share/qemu/ovmf-x86_64-opensuse-vars.bin
OVMF_VARS="$FILE.vars"
if echo "$FILE" | grep -q win10; then
	WIN10=1
elif echo "$FILE" | grep -q win; then
	WINOLD=1
fi
if echo "$FILE" | grep -q nbsd; then
	NBSD=1
fi
if echo "$FILE" | grep -q fact; then
	FACT=1
	EFI=1
fi
if echo "$FILE" | grep -q -- -efi; then
	EFI=1
fi
if echo "$FILE" | grep -q '32\.img$'; then
	BIT32=1
	EFI=0
fi
if echo "$FILE" | grep -q '\.img$'; then
	RAW=",format=raw"
fi

QEMU_VER=`qemu-kvm --version | sed 's@.*version \([0-9.]*\)[ ,(].*@\1@'`
QEMU_MAJOR=${QEMU_VER%%.*}

DRIVE="-drive file=$FILE$RAW"
if [ "$QEMU_MAJOR" -ge 2 ]; then
	DRIVE="$DRIVE,discard=unmap"
	if [ $WINOLD = 0 -a $NBSD = 0 ]; then
		DRIVE="$DRIVE,if=none,id=hd -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd"
	fi
fi

MEM=""
if ! echo $@ | grep -q -- '-m '; then
	MEM=`grep 'MemTotal:' /proc/meminfo|sed 's/^.*: *\([0-9]*\) *kB$/\1/'`
	MEM=$(($MEM/5/1024))

	if [ $MEM -lt 100 ]; then
		MEM=100
	fi

	MEM="-m $MEM"
fi

SMP=""
if ! echo $@ | grep -q -- '-smp '; then
	SMP=`grep '^processor' /proc/cpuinfo | tail -1 | sed 's@.* @@'`
	SMP=$(($SMP + 1))
	SMP="-smp $SMP"
fi

SOUND=""
if ( which pulseaudio &>/dev/null && pulseaudio --check) || which pipewire &>/dev/null; then
	if [ $WINOLD = 1 ]; then
		SOUND='-device AC97'
	else
		SOUND='-device intel-hda -device hda-duplex'
	fi
fi

function find_free_port() {
	FREE_PORT="$1"
	while :; do
		/usr/sbin/ss -tnl "sport = $FREE_PORT" | grep -q ":$FREE_PORT\>" || break
		FREE_PORT=$((FREE_PORT+1))
	done
	echo "$FREE_PORT"
}

if [ "$WANT_SPICE" = 1 ] && ( test "$SPICE_REMOTE" = 1 || which spicy >/dev/null 2>&1) ; then
	SPICE_PORT="/tmp/qemu-spice.$$.sock"
	# gl=on,
	SPICE_CONF="unix,addr=$SPICE_PORT"
	if [ "$SPICE_REMOTE" = 1 ]; then
		SPICE_PORT=`find_free_port 3333`
		SPICE_CONF="port=$SPICE_PORT"
	fi
	SPICE="-spice disable-ticketing,$SPICE_CONF -device virtio-serial -monitor pty "
	SPICE="$SPICE -chardev spicevmc,id=vdagent,name=vdagent -device virtserialport,chardev=vdagent,name=com.redhat.spice.0"
	SPICE="$SPICE -chardev spicevmc,name=usbredir,id=usbredirchardev1 -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1"
	SPICE="$SPICE -chardev spicevmc,name=usbredir,id=usbredirchardev2 -device usb-redir,chardev=usbredirchardev2,id=usbredirdev2"
fi

NET="-net nic,model=virtio"
USB=""
if [ $WINOLD = 1 ]; then
	USB="-device ich9-usb-ehci1,multifunction=on,id=ehci "
	USB="$USB -device ich9-usb-uhci1,masterbus=ehci.0,multifunction=on"
	USB="$USB -device ich9-usb-uhci2,masterbus=ehci.0,firstport=2,multifunction=on"
	USB="$USB -device ich9-usb-uhci3,masterbus=ehci.0,firstport=4,multifunction=on"
	NET="-net nic,model=rtl8139"
else
	USB="-device qemu-xhci,p2=8,p3=8"
fi

OTHER=""
TELNET_PORT=""
SERIAL_WAIT_RE=""
if [ $WINOLD = 1 -o $WIN10 = 1 ]; then
	NET="-net user,smb=/home/smb,hostfwd=tcp::`find_free_port 3389`-:3389 $NET"
	OTHER="-rtc base=localtime -vga qxl"
	if [ $WIN10 = 1 ]; then
		OTHER="$OTHER --device virtio-balloon"
	fi
else
	if [ $SCREEN = 1 ]; then
		SERIAL="pty"
		SERIAL_WAIT_RE='redirected to /dev/.*(label serial'
	else
		TELNET_PORT=`find_free_port 2323`
		SERIAL="telnet:localhost:$TELNET_PORT,server"
		SERIAL_WAIT_RE='QEMU waiting for connection.*telnet'
	fi
	NET="-net user,tftp=/home/xslaby/tftp,bootfile=/pxelinux.0,hostfwd=tcp::`find_free_port 2222`-:22,hostfwd=tcp::`find_free_port 3632`-:3632 $NET"
	OTHER="-serial $SERIAL --device virtio-balloon"
fi

if [ $EFI = 1 ]; then
	if [ -f "$OVMF_CODE" -a -f "$OVMF_VARS_SRC" ]; then
		if [ ! -f "$OVMF_VARS" ]; then
			cp -v "$OVMF_VARS_SRC" "$OVMF_VARS"
		fi
		OTHER="$OTHER -drive if=pflash,format=raw,unit=0,readonly,file=$OVMF_CODE"
		OTHER="$OTHER -drive if=pflash,format=raw,unit=1,file=$OVMF_VARS"
	fi
fi

if [ $FACT = 1 ]; then
	OTHER="$OTHER -device virtio-tablet-pci -device virtio-vga,virgl=on"
else
	OTHER="$OTHER -device usb-tablet"
fi

PID_FILE="$TMPDIR/qemu.pid.$$"
QEMU_ARGS="-k en-us $SMP $MEM -daemonize -pidfile $PID_FILE -device virtio-rng-pci $DRIVE $SOUND $USB $SPICE $NET $OTHER"

QEMU_RUN="qemu-kvm $QEMU_ARGS"
SCR="$TMPDIR/qemu.$$"

function finish() {
	echo Cleaning up
	rm -f "$SCR" "$PID_FILE" &>/dev/null
	if [ -n "$SPICE_PORT" ]; then
		rm -f "$SPICE_PORT" &>/dev/null
	fi
}

trap finish EXIT
finish

echo $QEMU_RUN "$@"
if [ -n "$TELNET_PORT" ]; then
	$QEMU_RUN "$@" |& tee $SCR >&2 &
else
	$QEMU_RUN "$@" |& tee $SCR >&2
fi
if [ $? -ne 0 ]; then
	exit 1
fi

if [ -n "$SERIAL_WAIT_RE" ]; then
	while ! [ -f "$PID_FILE" -a -f "$SCR" ] || ! grep -q "$SERIAL_WAIT_RE" "$SCR"; do
		usleep 100000
	done
fi
QEMU=`cat $PID_FILE`
rm -f $PID_FILE

if [ -n "$SPICE_PORT" -a "$SPICE_REMOTE" != 1 ]; then
	(while :; do
		spicy --uri="spice+unix://$SPICE_PORT" 2>/dev/null;
		if [ $? -lt 128 ]; then
			break;
		fi;
	done;
	kill $QEMU 2>/dev/null) &
fi

if [ -n "$TELNET_PORT" ]; then
	telnet localhost $TELNET_PORT
elif [ $SCREEN = 1 ]; then
	TTY=`sed -ne '/redirected to .*label serial/ s@.*redirected to \(/dev[^ ]*\).*@\1@ p' $SCR`
	if [ -c "$TTY" ]; then
		screen "$TTY"
	fi
fi

function kill_qemu() {
	echo Killing qemu
	kill $QEMU
	exit 0
}

trap kill_qemu INT TERM

# wait $QEMU  -- won't work
echo Waiting for qemu to finish
while [ -d /proc/$QEMU ]; do
	sleep 1
done
