mirror of
https://github.com/oSoWoSo/DistroHopper.git
synced 2024-08-14 22:46:53 +00:00
628d1ee088
Co-authored-by: Sec Scout <secscout@localhost.localdomain>
675 lines
22 KiB
Bash
Executable file
675 lines
22 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
export LC_ALL=C
|
|
|
|
function web_get() {
|
|
local URL="${1}"
|
|
local FILE=$(echo "${URL##*/}")
|
|
if [ ! -e "${VMDIR}/${FILE}" ]; then
|
|
wget -q -c "${URL}" -O "${VMDIR}/${FILE}"
|
|
if [ $? -ne 0 ]; then
|
|
echo "ERROR! Failed to download ${URL}"
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function disk_delete() {
|
|
if [ -e "${disk_img}" ]; then
|
|
rm "${disk_img}"
|
|
echo "SUCCESS! Deleted ${disk_img}"
|
|
else
|
|
echo "NOTE! ${disk_img} not found. Doing nothing."
|
|
fi
|
|
local VMNAME=$(basename "${VM}" .conf)
|
|
local SHORTCUT_DIR="/home/${USER}/.local/share/applications/"
|
|
if [ -e ${SHORTCUT_DIR}/${VMNAME}.desktop ]; then
|
|
rm -v "${SHORTCUT_DIR}/${VMNAME}.desktop"
|
|
echo "Deleted ${VM} desktop shortcut"
|
|
fi
|
|
}
|
|
|
|
function snapshot_apply() {
|
|
local TAG="${1}"
|
|
if [ -z "${TAG}" ]; then
|
|
echo "ERROR! No snapshot tag provided."
|
|
exit
|
|
fi
|
|
|
|
if [ -e "${disk_img}" ]; then
|
|
${QEMU_IMG} snapshot -q -a "${TAG}" "${disk_img}"
|
|
if [ $? -eq 0 ]; then
|
|
echo "SUCCESS! Applied snapshot ${TAG} to ${disk_img}"
|
|
else
|
|
echo "ERROR! Failed to apply snapshot ${TAG} to ${disk_img}"
|
|
fi
|
|
else
|
|
echo "NOTE! ${disk_img} not found. Doing nothing."
|
|
fi
|
|
}
|
|
|
|
function snapshot_create() {
|
|
local TAG="${1}"
|
|
if [ -z "${TAG}" ]; then
|
|
echo "ERROR! No snapshot tag provided."
|
|
exit
|
|
fi
|
|
|
|
if [ -e "${disk_img}" ]; then
|
|
${QEMU_IMG} snapshot -q -c "${TAG}" "${disk_img}"
|
|
if [ $? -eq 0 ]; then
|
|
echo "SUCCESS! Created snapshot ${TAG} of ${disk_img}"
|
|
else
|
|
echo "ERROR! Failed to create snapshot ${TAG} of ${disk_img}"
|
|
fi
|
|
else
|
|
echo "NOTE! ${disk_img} not found. Doing nothing."
|
|
fi
|
|
}
|
|
|
|
function snapshot_delete() {
|
|
local TAG="${1}"
|
|
if [ -z "${TAG}" ]; then
|
|
echo "ERROR! No snapshot tag provided."
|
|
exit
|
|
fi
|
|
|
|
if [ -e "${disk_img}" ]; then
|
|
${QEMU_IMG} snapshot -q -d "${TAG}" "${disk_img}"
|
|
if [ $? -eq 0 ]; then
|
|
echo "SUCCESS! Deleted snapshot ${TAG} of ${disk_img}"
|
|
else
|
|
echo "ERROR! Failed to delete snapshot ${TAG} of ${disk_img}"
|
|
fi
|
|
else
|
|
echo "NOTE! ${disk_img} not found. Doing nothing."
|
|
fi
|
|
}
|
|
|
|
function snapshot_info() {
|
|
if [ -e "${disk_img}" ]; then
|
|
${QEMU_IMG} info "${disk_img}"
|
|
fi
|
|
}
|
|
|
|
function get_port() {
|
|
local PORT_START=$1
|
|
local PORT_RANGE=$2
|
|
while true; do
|
|
local CANDIDATE=$[${PORT_START} + (${RANDOM} % ${PORT_RANGE})]
|
|
(echo "" >/dev/tcp/127.0.0.1/${CANDIDATE}) >/dev/null 2>&1
|
|
if [ $? -ne 0 ]; then
|
|
echo "${CANDIDATE}"
|
|
break
|
|
fi
|
|
done
|
|
}
|
|
|
|
enable_usb_passthrough() {
|
|
local DEVICE=""
|
|
local USB_BUS=""
|
|
local USB_DEV=""
|
|
local USB_NAME=""
|
|
local VENDOR_ID=""
|
|
local PRODUCT_ID=""
|
|
local TEMP_SCRIPT=$(mktemp)
|
|
local EXEC_SCRIPT=0
|
|
|
|
# Have any USB devices been requested for pass-through?
|
|
if (( ${#usb_devices[@]} )); then
|
|
echo " - USB: Device pass-through requested:"
|
|
echo "#!/usr/bin/env bash" > "${TEMP_SCRIPT}"
|
|
for DEVICE in "${usb_devices[@]}"; do
|
|
VENDOR_ID=$(echo ${DEVICE} | cut -d':' -f1)
|
|
PRODUCT_ID=$(echo ${DEVICE} | cut -d':' -f2)
|
|
USB_BUS=$(lsusb -d ${VENDOR_ID}:${PRODUCT_ID} | cut -d' ' -f2)
|
|
USB_DEV=$(lsusb -d ${VENDOR_ID}:${PRODUCT_ID} | cut -d' ' -f4 | cut -d':' -f1)
|
|
USB_NAME=$(lsusb -d ${VENDOR_ID}:${PRODUCT_ID} | cut -d' ' -f7-)
|
|
echo " - ${USB_NAME}"
|
|
USB_PASSTHROUGH="${USB_PASSTHROUGH} -device usb-host,vendorid=0x${VENDOR_ID},productid=0x${PRODUCT_ID},bus=usb.0"
|
|
if [ ! -w /dev/bus/usb/${USB_BUS}/${USB_DEV} ]; then
|
|
local EXEC_SCRIPT=1
|
|
echo "chown root:${USER} /dev/bus/usb/${USB_BUS}/${USB_DEV}" >> "${TEMP_SCRIPT}"
|
|
fi
|
|
done
|
|
|
|
if [ ${EXEC_SCRIPT} -eq 1 ]; then
|
|
chmod +x "${TEMP_SCRIPT}"
|
|
echo " Requested USB device(s) are NOT accessible."
|
|
echo " ${TEMP_SCRIPT} will be executed to enable access:"
|
|
echo
|
|
cat ${TEMP_SCRIPT}
|
|
echo
|
|
sudo "${TEMP_SCRIPT}"
|
|
if [ $? -ne 0 ]; then
|
|
echo " WARNING! Enabling USB device access failed."
|
|
fi
|
|
else
|
|
echo " Requested USB device(s) are accessible."
|
|
fi
|
|
rm -f "${TEMP_SCRIPT}"
|
|
fi
|
|
}
|
|
|
|
function vm_boot() {
|
|
local VMNAME=$(basename "${VM}" .conf)
|
|
local VMDIR=$(dirname "${disk_img}")
|
|
local CPU="-cpu host,kvm=on"
|
|
local GUEST_TWEAKS=""
|
|
local DISPLAY_DEVICE=""
|
|
local VIDEO=""
|
|
local GL="on"
|
|
local VIRGL="on"
|
|
local OUTPUT="sdl"
|
|
local OUTPUT_EXTRA=""
|
|
local QEMU_VER=$(${QEMU} -version | head -n1 | cut -d' ' -f4 | cut -d'(' -f1)
|
|
echo "Starting ${VM}"
|
|
echo " - QEMU: ${QEMU} v${QEMU_VER}"
|
|
|
|
# Force to lowercase.
|
|
boot=$(echo ${boot,,})
|
|
|
|
# Always Boot macOS using EFI
|
|
if [ "${guest_os}" == "macos" ]; then
|
|
boot="efi"
|
|
fi
|
|
|
|
if [ "${boot}" == "efi" ] || [ "${boot}" == "uefi" ]; then
|
|
if [ -e "${VIRGIL_PATH}/usr/share/qemu/edk2-x86_64-code.fd" ] ; then
|
|
if [ "${guest_os}" == "macos" ]; then
|
|
web_get "https://github.com/foxlet/macOS-Simple-KVM/raw/master/ESP.qcow2"
|
|
web_get "https://github.com/foxlet/macOS-Simple-KVM/raw/master/firmware/OVMF_CODE.fd"
|
|
web_get "https://github.com/foxlet/macOS-Simple-KVM/raw/master/firmware/OVMF_VARS-1024x768.fd"
|
|
local EFI_CODE="${VMDIR}/OVMF_CODE.fd"
|
|
local EFI_VARS="${VMDIR}/OVMF_VARS-1024x768.fd"
|
|
else
|
|
local EFI_CODE="${VIRGIL_PATH}/usr/share/qemu/edk2-x86_64-code.fd"
|
|
local EFI_VARS="${VMDIR}/${VMNAME}-vars.fd"
|
|
if [ ! -e "${EFI_VARS}" ]; then
|
|
cp "${VIRGIL_PATH}/usr/share/qemu/edk2-i386-vars.fd" "${EFI_VARS}"
|
|
fi
|
|
fi
|
|
echo " - BOOT: EFI"
|
|
else
|
|
echo " - BOOT: Legacy BIOS"
|
|
echo " - EFI Booting requested but no EFI firmware found."
|
|
fi
|
|
else
|
|
echo " - BOOT: Legacy BIOS"
|
|
fi
|
|
|
|
# Force to lowercase.
|
|
guest_os=$(echo ${guest_os,,})
|
|
# Make any OS specific adjustments
|
|
case ${guest_os} in
|
|
linux)
|
|
DISPLAY_DEVICE="virtio-vga"
|
|
;;
|
|
macos)
|
|
CPU="-cpu Penryn,vendor=GenuineIntel,kvm=on,+aes,+avx,+avx2,+bmi1,+bmi2,+fma,+invtsc,+movbe,+pcid,+smep,+sse3,+sse4.2,+xgetbv1,+xsave,+xsavec,+xsaveopt"
|
|
readonly ROT_OSK="bheuneqjbexolgurfrjbeqfthneqrqcyrnfrqbagfgrny(p)NccyrPbzchgreVap"
|
|
readonly OSK=$(echo ${ROT_OSK} | rot13)
|
|
GUEST_TWEAKS="-device isa-applesmc,osk=${OSK}"
|
|
DISPLAY_DEVICE="VGA"
|
|
VIRGL="off"
|
|
;;
|
|
windows)
|
|
CPU="${CPU},hv_time"
|
|
GUEST_TWEAKS="-no-hpet"
|
|
DISPLAY_DEVICE="qxl-vga"
|
|
;;
|
|
*)
|
|
echo "ERROR! Unrecognised guest OS: ${guest_os}"
|
|
exit
|
|
;;
|
|
esac
|
|
echo " - Guest: ${guest_os^} optimised"
|
|
|
|
echo " - Disk: ${disk_img} (${disk})"
|
|
if [ ! -f "${disk_img}" ]; then
|
|
# If there is no disk image, create a new image.
|
|
mkdir -p "${VMDIR}" 2>/dev/null
|
|
${QEMU_IMG} create -q -f qcow2 "${disk_img}" "${disk}"
|
|
if [ $? -ne 0 ]; then
|
|
echo "ERROR! Failed to create ${disk_img}"
|
|
exit 1
|
|
fi
|
|
if [ -z "${iso}" ] && [ -z "${img}" ]; then
|
|
echo "ERROR! You haven't specified a .iso or .img image to boot from."
|
|
exit 1
|
|
fi
|
|
echo " Just created, booting from ${iso}${img}"
|
|
if [ $? -ne 0 ]; then
|
|
echo "ERROR! Failed to create ${disk_img} of ${disk}. Stopping here."
|
|
exit 1
|
|
fi
|
|
elif [ -e "${disk_img}" ]; then
|
|
# Check there isn't already a process attached to the disk image.
|
|
QEMU_LOCK_TEST=$(${QEMU_IMG} info "${disk_img}" 2>/dev/null)
|
|
if [ $? -ne 0 ]; then
|
|
echo " Failed to get "write" lock. Is another process using the disk?"
|
|
exit 1
|
|
else
|
|
DISK_CURR_SIZE=$(stat -c%s "${disk_img}")
|
|
if [ ${DISK_CURR_SIZE} -le ${DISK_MIN_SIZE} ]; then
|
|
echo " Looks unused, booting from ${iso}${img}"
|
|
if [ -z "${iso}" ] && [ -z "${img}" ]; then
|
|
echo "ERROR! You haven't specified a .iso or .img image to boot from."
|
|
exit 1
|
|
fi
|
|
else
|
|
# If there is a disk image, that appears to have an install
|
|
# then do not boot from the iso/img
|
|
iso=""
|
|
img=""
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Has the status quo been requested?
|
|
if [ "${STATUS_QUO}" == "-snapshot" ] && [ -z "${iso}" ]; then
|
|
echo " Existing disk state will be preserved, no writes will be committed."
|
|
fi
|
|
|
|
if [ -n "${iso}" ] && [ -e "${iso}" ]; then
|
|
echo " - Boot: ${iso}"
|
|
fi
|
|
|
|
if [ -n "${driver_iso}" ] && [ -e "${driver_iso}" ]; then
|
|
echo " - Drivers: ${driver_iso}"
|
|
fi
|
|
|
|
local CORES_VM="1"
|
|
if [ -z "$cpu_cores" ]; then
|
|
local CORES_HOST=$(nproc --all)
|
|
if [ ${CORES_HOST} -ge 8 ]; then
|
|
CORES_VM="4"
|
|
elif [ ${CORES_HOST} -ge 4 ]; then
|
|
CORES_VM="2"
|
|
fi
|
|
else
|
|
CORES_VM="$cpu_cores"
|
|
fi
|
|
local SMP="-smp ${CORES_VM},sockets=1,cores=${CORES_VM},threads=1"
|
|
echo " - CPU: ${CORES_VM} Core(s)"
|
|
|
|
local RAM_VM="2G"
|
|
if [ -z "$ram" ]; then
|
|
local RAM_HOST=$(free --mega -h | grep Mem | cut -d':' -f2 | cut -d'G' -f1 | sed 's/ //g')
|
|
#Round up - https://github.com/wimpysworld/quickemu/issues/11
|
|
RAM_HOST=$(printf '%.*f\n' 0 ${RAM_HOST})
|
|
if [ ${RAM_HOST} -ge 64 ]; then
|
|
RAM_VM="4G"
|
|
elif [ ${RAM_HOST} -ge 16 ]; then
|
|
RAM_VM="3G"
|
|
fi
|
|
else
|
|
RAM_VM="$ram"
|
|
fi
|
|
echo " - RAM: ${RAM_VM}"
|
|
|
|
local X_RES=1152
|
|
local Y_RES=648
|
|
if [ "${XDG_SESSION_TYPE}" == "x11" ]; then
|
|
local LOWEST_WIDTH=$(xrandr --listmonitors | grep -v Monitors | cut -d' ' -f4 | cut -d'/' -f1 | sort | head -n1)
|
|
if [ ${FULLSCREEN} ]; then
|
|
X_RES=$(xrandr --listmonitors | grep -v Monitors | cut -d' ' -f4 | cut -d'/' -f1 | sort | head -n1)
|
|
Y_RES=$(xrandr --listmonitors | grep -v Monitors | cut -d' ' -f4 | cut -d'/' -f2 | cut -d'x' -f2 | sort | head -n1)
|
|
elif [ ${LOWEST_WIDTH} -ge 3840 ]; then
|
|
X_RES=3200
|
|
Y_RES=1800
|
|
elif [ ${LOWEST_WIDTH} -ge 2560 ]; then
|
|
X_RES=2048
|
|
Y_RES=1152
|
|
elif [ ${LOWEST_WIDTH} -ge 1920 ]; then
|
|
X_RES=1664
|
|
Y_RES=936
|
|
elif [ ${LOWEST_WIDTH} -ge 1280 ]; then
|
|
X_RES=1152
|
|
Y_RES=648
|
|
fi
|
|
fi
|
|
|
|
# GL is not working with GTK currently
|
|
if [ "${OUTPUT}" == "gtk" ]; then
|
|
GL="es"
|
|
OUTPUT_EXTRA=",grab-on-hover=on,zoom-to-fit=on"
|
|
else
|
|
echo " - Screen: ${X_RES}x${Y_RES}"
|
|
fi
|
|
|
|
if [ "${DISPLAY_DEVICE}" == "qxl-vga" ]; then
|
|
VIDEO="-device ${DISPLAY_DEVICE},xres=${X_RES},yres=${Y_RES}, ${FULLSCREEN}"
|
|
elif [ "${DISPLAY_DEVICE}" == "virtio-vga" ]; then
|
|
VIDEO="-device ${DISPLAY_DEVICE},virgl=${VIRGL},xres=${X_RES},yres=${Y_RES}, ${FULLSCREEN}"
|
|
else
|
|
VIDEO="-device VGA,vgamem_mb=128,xres=${X_RES},yres=${Y_RES}, ${FULLSCREEN}"
|
|
fi
|
|
|
|
echo " - Video: ${DISPLAY_DEVICE}"
|
|
echo " - GL: ${GL^^}"
|
|
echo " - Virgil3D: ${VIRGL^^}"
|
|
echo " - Display: ${OUTPUT^^}"
|
|
|
|
# Set the hostname of the VM
|
|
local NET="user,hostname=${VMNAME}"
|
|
|
|
# If smbd is available, and --no-smb is not set, export $HOME to the guest via samba
|
|
if [[ -e "${VIRGIL_PATH}/usr/sbin/smbd" && -z ${NO_SMB} ]]; then
|
|
NET="${NET},smb=${HOME}"
|
|
fi
|
|
|
|
if [[ ${NET} == *"smb"* ]]; then
|
|
echo " - smbd: ${HOME} will be exported to the guest via smb://10.0.2.4/qemu"
|
|
elif [[ ${NO_SMB} -eq 1 ]]; then
|
|
echo " - smbd: ${HOME} will not be exported to the guest. '--no-smb' is set."
|
|
else
|
|
echo " - smbd: ${HOME} will not be exported to the guest. 'smbd' not found."
|
|
fi
|
|
|
|
# Find a free port to expose ssh to the guest
|
|
local PORT=$(get_port 22220 9)
|
|
if [ -n "${PORT}" ]; then
|
|
NET="${NET},hostfwd=tcp::${PORT}-:22"
|
|
echo " - ssh: ${PORT}/tcp is connected. Login via 'ssh user@localhost -p ${PORT}'"
|
|
else
|
|
echo " - ssh: All ports for exposing ssh have been exhausted."
|
|
fi
|
|
|
|
# Have any port forwards been requested?
|
|
if (( ${#port_forwards[@]} )); then
|
|
echo " - PORTS: Port forwards requested:"
|
|
for FORWARD in "${port_forwards[@]}"; do
|
|
HOST_PORT=$(echo ${FORWARD} | cut -d':' -f1)
|
|
GUEST_PORT=$(echo ${FORWARD} | cut -d':' -f2)
|
|
echo " - ${HOST_PORT} => ${GUEST_PORT}"
|
|
NET="${NET},hostfwd=tcp::${HOST_PORT}-:${GUEST_PORT}"
|
|
done
|
|
fi
|
|
|
|
# Find a free port for spice
|
|
local SPICE_PORT=$(get_port 5930 9)
|
|
if [ ! -n "${SPICE_PORT}" ]; then
|
|
echo " - spice: All spice ports have been exhausted."
|
|
fi
|
|
|
|
enable_usb_passthrough
|
|
|
|
# Boot the iso image
|
|
if [ "${boot}" == "efi" ] || [ "${boot}" == "uefi" ]; then
|
|
if [ "${guest_os}" == "macos" ]; then
|
|
if [ -z "${img}" ]; then
|
|
${QEMU} \
|
|
-name ${VMNAME},process=${VMNAME} \
|
|
-enable-kvm -machine q35 ${GUEST_TWEAKS} \
|
|
${CPU} ${SMP} \
|
|
-m ${RAM_VM} -device virtio-balloon \
|
|
-drive if=pflash,format=raw,readonly,file="${EFI_CODE}" \
|
|
-drive if=pflash,format=raw,file="${EFI_VARS}" \
|
|
-drive id=ESP,cache=directsync,aio=native,if=none,format=qcow2,file="${VMDIR}/ESP.qcow2" \
|
|
-device virtio-blk-pci,drive=ESP,scsi=off \
|
|
-drive id=SystemDisk,cache=directsync,aio=native,if=none,format=qcow2,file="${disk_img}" ${STATUS_QUO} \
|
|
-device virtio-blk-pci,drive=SystemDisk,scsi=off \
|
|
${VIDEO} -display ${OUTPUT},gl=${GL}${OUTPUT_EXTRA} \
|
|
-device usb-ehci,id=usb -device usb-kbd,bus=usb.0 -device usb-tablet,bus=usb.0 ${USB_PASSTHROUGH} \
|
|
-device vmxnet3,netdev=nic -netdev ${NET},id=nic \
|
|
-audiodev pa,id=pa,server=unix:${XDG_RUNTIME_DIR}/pulse/native,out.stream-name=${LAUNCHER}-${VMNAME},in.stream-name=${LAUNCHER}-${VMNAME} \
|
|
-device intel-hda -device hda-duplex,audiodev=pa,mixer=off \
|
|
-rtc base=localtime,clock=host \
|
|
-serial mon:stdio \
|
|
"${@}"
|
|
else
|
|
${QEMU} \
|
|
-name ${VMNAME},process=${VMNAME} \
|
|
-enable-kvm -machine q35 ${GUEST_TWEAKS} \
|
|
${CPU} ${SMP} \
|
|
-m ${RAM_VM} -device virtio-balloon \
|
|
-drive if=pflash,format=raw,readonly,file="${EFI_CODE}" \
|
|
-drive if=pflash,format=raw,file="${EFI_VARS}" \
|
|
-drive id=ESP,cache=directsync,aio=native,if=none,format=qcow2,file="${VMDIR}/ESP.qcow2" \
|
|
-device virtio-blk-pci,drive=ESP,scsi=off \
|
|
-drive id=InstallMedia,cache=directsync,aio=native,if=none,format=raw,readonly,file="${img}" \
|
|
-device virtio-blk-pci,drive=InstallMedia,scsi=off \
|
|
-drive id=SystemDisk,cache=directsync,aio=native,if=none,format=qcow2,file="${disk_img}" ${STATUS_QUO} \
|
|
-device virtio-blk-pci,drive=SystemDisk,scsi=off \
|
|
${VIDEO} -display ${OUTPUT},gl=${GL}${OUTPUT_EXTRA} \
|
|
-device usb-ehci,id=usb -device usb-kbd,bus=usb.0 -device usb-tablet,bus=usb.0 ${USB_PASSTHROUGH} \
|
|
-device vmxnet3,netdev=nic -netdev ${NET},id=nic \
|
|
-audiodev pa,id=pa,server=unix:${XDG_RUNTIME_DIR}/pulse/native,out.stream-name=${LAUNCHER}-${VMNAME},in.stream-name=${LAUNCHER}-${VMNAME} \
|
|
-device intel-hda -device hda-duplex,audiodev=pa,mixer=off \
|
|
-rtc base=localtime,clock=host \
|
|
-serial mon:stdio \
|
|
"${@}"
|
|
fi
|
|
else
|
|
${QEMU} \
|
|
-name ${VMNAME},process=${VMNAME} \
|
|
-enable-kvm -machine q35 ${GUEST_TWEAKS} \
|
|
${CPU} ${SMP} \
|
|
-m ${RAM_VM} -device virtio-balloon \
|
|
-drive if=pflash,format=raw,readonly,file="${EFI_CODE}" \
|
|
-drive if=pflash,format=raw,file="${EFI_VARS}" \
|
|
-drive media=cdrom,index=0,file="${iso}" \
|
|
-drive media=cdrom,index=1,file="${driver_iso}" \
|
|
-drive if=none,id=drive0,cache=directsync,aio=native,format=qcow2,file="${disk_img}" \
|
|
-device virtio-blk-pci,drive=drive0,scsi=off ${STATUS_QUO} \
|
|
${VIDEO} -display ${OUTPUT},gl=${GL}${OUTPUT_EXTRA} \
|
|
-device qemu-xhci,id=usb,p2=8,p3=8 -device usb-kbd,bus=usb.0 -device usb-tablet,bus=usb.0 ${USB_PASSTHROUGH} \
|
|
-device virtio-net,netdev=nic -netdev ${NET},id=nic \
|
|
-audiodev pa,id=pa,server=unix:${XDG_RUNTIME_DIR}/pulse/native,out.stream-name=${LAUNCHER}-${VMNAME},in.stream-name=${LAUNCHER}-${VMNAME} \
|
|
-device intel-hda -device hda-duplex,audiodev=pa,mixer=off \
|
|
-rtc base=localtime,clock=host \
|
|
-object rng-random,id=rng0,filename=/dev/urandom \
|
|
-device virtio-rng-pci,rng=rng0 \
|
|
-spice port=${SPICE_PORT},disable-ticketing \
|
|
-device virtio-serial-pci \
|
|
-device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0 \
|
|
-chardev spicevmc,id=spicechannel0,name=vdagent \
|
|
-serial mon:stdio \
|
|
"${@}"
|
|
fi
|
|
else
|
|
${QEMU} \
|
|
-name ${VMNAME},process=${VMNAME} \
|
|
-enable-kvm -machine q35 ${GUEST_TWEAKS} \
|
|
${CPU} ${SMP} \
|
|
-m ${RAM_VM} -device virtio-balloon \
|
|
-drive media=cdrom,index=0,file="${iso}" \
|
|
-drive media=cdrom,index=1,file="${driver_iso}" \
|
|
-drive if=none,id=drive0,cache=directsync,aio=native,format=qcow2,file="${disk_img}" \
|
|
-device virtio-blk-pci,drive=drive0,scsi=off ${STATUS_QUO} \
|
|
${VIDEO} -display ${OUTPUT},gl=${GL}${OUTPUT_EXTRA} \
|
|
-device qemu-xhci,id=usb,p2=8,p3=8 -device usb-kbd,bus=usb.0 -device usb-tablet,bus=usb.0 ${USB_PASSTHROUGH} \
|
|
-device virtio-net,netdev=nic -netdev ${NET},id=nic \
|
|
-audiodev pa,id=pa,server=unix:${XDG_RUNTIME_DIR}/pulse/native,out.stream-name=${LAUNCHER}-${VMNAME},in.stream-name=${LAUNCHER}-${VMNAME} \
|
|
-device intel-hda -device hda-duplex,audiodev=pa,mixer=off \
|
|
-rtc base=localtime,clock=host \
|
|
-object rng-random,id=rng0,filename=/dev/urandom \
|
|
-device virtio-rng-pci,rng=rng0 \
|
|
-spice port=${SPICE_PORT},disable-ticketing \
|
|
-device virtio-serial-pci \
|
|
-device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0 \
|
|
-chardev spicevmc,id=spicechannel0,name=vdagent \
|
|
-serial mon:stdio \
|
|
"${@}"
|
|
fi
|
|
}
|
|
|
|
function shortcut_create {
|
|
local VMNAME=$(basename "${VM}" .conf)
|
|
local LAUNCHER_DIR="$(dirname "$(realpath "$0")")"
|
|
local filename="/home/${USER}/.local/share/applications/${VMNAME}.desktop"
|
|
cat << EOF > ${filename}
|
|
[Desktop Entry]
|
|
Version=1.0
|
|
Type=Application
|
|
Terminal=true
|
|
Exec=${LAUNCHER_DIR}/${LAUNCHER} --vm ${VM}
|
|
Name=${VMNAME}
|
|
Icon=/snap/qemu-virgil/current/meta/gui/icon.png
|
|
EOF
|
|
echo "Created ${VMNAME}.desktop file"
|
|
}
|
|
|
|
function usage() {
|
|
echo
|
|
echo "Usage"
|
|
echo " ${LAUNCHER} --vm ubuntu.conf"
|
|
echo
|
|
echo "You can also pass optional parameters"
|
|
echo " --delete : Delete the disk image."
|
|
echo " --shortcut : Create a desktop shortcut"
|
|
echo " --snapshot apply <tag> : Apply/restore a snapshot."
|
|
echo " --snapshot create <tag> : Create a snapshot."
|
|
echo " --snapshot delete <tag> : Delete a snapshot."
|
|
echo " --snapshot info : Show disk/snapshot info."
|
|
echo " --status-quo : Do not commit any changes to disk/snapshot."
|
|
echo " --system-qemu : Use system install of qemu."
|
|
echo " --fullscreen : Starts VM in full screen mode"
|
|
echo " --no-smb : Do not expose the home directory via SMB."
|
|
exit 1
|
|
}
|
|
|
|
# Lowercase variables are used in the VM config file only
|
|
boot="legacy"
|
|
guest_os="linux"
|
|
img=""
|
|
iso=""
|
|
driver_iso=""
|
|
disk_img=""
|
|
disk="64G"
|
|
usb_devices=()
|
|
ram=""
|
|
cpu_cores=""
|
|
|
|
FULLSCREEN=""
|
|
DELETE=0
|
|
ENABLE_EFI=0
|
|
SNAPSHOT_ACTION=""
|
|
SNAPSHOT_TAG=""
|
|
STATUS_QUO=""
|
|
USB_PASSTHOUGH=""
|
|
VM=""
|
|
SHORTCUT=0
|
|
|
|
readonly LAUNCHER=$(basename "${0}")
|
|
readonly DISK_MIN_SIZE=$((197632 * 8))
|
|
QEMU="/snap/bin/qemu-virgil"
|
|
QEMU_IMG="/snap/bin/qemu-virgil.qemu-img"
|
|
VIRGIL_PATH="/snap/qemu-virgil/current"
|
|
|
|
# Take command line arguments
|
|
if [ $# -lt 1 ]; then
|
|
usage
|
|
exit 0
|
|
else
|
|
while [ $# -gt 0 ]; do
|
|
case "${1}" in
|
|
-delete|--delete)
|
|
DELETE=1
|
|
shift;;
|
|
-snapshot|--snapshot)
|
|
SNAPSHOT_ACTION="${2}"
|
|
if [ -z "${SNAPSHOT_ACTION}" ]; then
|
|
echo "ERROR! No snapshot action provided."
|
|
exit 1
|
|
fi
|
|
shift
|
|
SNAPSHOT_TAG="${2}"
|
|
if [ -z "${SNAPSHOT_TAG}" ] && [ "${SNAPSHOT_ACTION}" != "info" ]; then
|
|
echo "ERROR! No snapshot tag provided."
|
|
exit 1
|
|
fi
|
|
shift
|
|
shift;;
|
|
-status-quo|--status-quo)
|
|
STATUS_QUO="-snapshot"
|
|
shift;;
|
|
-fullscreen|--fullscreen)
|
|
FULLSCREEN="--full-screen"
|
|
shift;;
|
|
-vm|--vm)
|
|
VM="${2}"
|
|
shift
|
|
shift;;
|
|
-shortcut|--shortcut)
|
|
SHORTCUT=1
|
|
shift;;
|
|
-system-qemu|--system-qemu)
|
|
SYSTEM_QEMU=1
|
|
shift;;
|
|
-no-smb|--no-smb)
|
|
NO_SMB=1
|
|
shift;;
|
|
-h|--h|-help|--help)
|
|
usage;;
|
|
*)
|
|
echo "ERROR! \"${1}\" is not a supported parameter."
|
|
usage;;
|
|
esac
|
|
done
|
|
fi
|
|
|
|
if [ -n "$SYSTEM_QEMU" ]; then
|
|
QEMU=$(which qemu-system-x86_64)
|
|
QEMU_IMG=$(which qemu-img)
|
|
VIRGIL_PATH=""
|
|
if [ ! -e "${QEMU}" ] && [ ! -e "${QEMU_IMG}" ]; then
|
|
echo "ERROR! qemu not found. Please install qemu."
|
|
exit 1
|
|
fi
|
|
echo "WARNING! using system install of qemu. Please make sure virgil is supported."
|
|
else
|
|
# Check we have qemu-virgil available
|
|
if [ ! -e "${QEMU}" ] && [ ! -e "${QEMU_IMG}" ]; then
|
|
echo "ERROR! qemu-virgil not found. Please install the qemu-virgil snap."
|
|
echo " https://snapcraft.io/qemu-virgil"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [ -n "${VM}" ] && [ -e "${VM}" ]; then
|
|
source "${VM}"
|
|
if [ -z "${disk_img}" ]; then
|
|
echo "ERROR! No disk_img defined."
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "ERROR! Virtual machine configuration not found."
|
|
usage
|
|
fi
|
|
|
|
if [ ${DELETE} -eq 1 ]; then
|
|
disk_delete
|
|
exit
|
|
fi
|
|
|
|
if [ -n "${SNAPSHOT_ACTION}" ]; then
|
|
case ${SNAPSHOT_ACTION} in
|
|
apply)
|
|
snapshot_apply "${SNAPSHOT_TAG}"
|
|
snapshot_info
|
|
exit;;
|
|
create)
|
|
snapshot_create "${SNAPSHOT_TAG}"
|
|
snapshot_info
|
|
exit;;
|
|
delete)
|
|
snapshot_delete "${SNAPSHOT_TAG}"
|
|
snapshot_info
|
|
exit;;
|
|
info)
|
|
snapshot_info
|
|
exit;;
|
|
*)
|
|
echo "ERROR! \"${SNAPSHOT_ACTION}\" is not a supported snapshot action."
|
|
usage;;
|
|
esac
|
|
fi
|
|
|
|
if [ ${SHORTCUT} -eq 1 ]; then
|
|
# echo "VM=" ${VM}
|
|
shortcut_create
|
|
exit
|
|
fi
|
|
|
|
vm_boot
|