commit 26288476766a7834e2c9aebeb868323d41a4adfa Author: Sebastian Hugentobler Date: Thu Jun 22 09:50:54 2023 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..765efc1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*~ +.DS_Store +*.pkg.tar.zst +*.pkg.tar.zst.sig diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 0000000..c790860 --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,20 @@ +# Maintainer: Sebastian Hugentobler + +pkgname=vm +pkgver=0.1.0 +pkgrel=1 +pkgdesc="Run qemu vms from configurations." +arch=("any") +url="https://code.vanwa.ch" +license=('MIT') +depends=("qemu-full" "swtpm") +source=( + "vm" +) +sha256sums=("1a6ee0ee645689953226154c3d74d23e49f1b6a5609c3ed9b344c1f2c278f0c5") + +package() { + cd "$srcdir/" + + install -Dm 755 vm "$pkgdir/usr/bin/vm" +} diff --git a/vm b/vm new file mode 100755 index 0000000..3e2c852 --- /dev/null +++ b/vm @@ -0,0 +1,157 @@ +#!/usr/bin/env sh +set -o errexit + +: "${XDG_CONFIG_HOME:="$HOME/.config"}" + +config_dir="$XDG_CONFIG_HOME/vm/" +tmpdir="${TMPDIR:-/tmp}" + +check_mandatory_keys() { + mandatory_keys="emu_type memory cpu_cores disk" + for k in $mandatory_keys; do + if eval '[ -z "${'$k'+1}" ]'; then + echo "key \"$k\" is mandatory" + exit 1 + fi + done +} + +read_config() { + if [ -f "$config_file" ]; then + # shellcheck disable=1090 + . "$config_file" + check_mandatory_keys + else + echo "$config_file not found" + exit 1 + fi +} + +display_help() { + echo "Usage: $0 [option...] " >&2 + echo + echo " -c, configuration name (reads the config of a file \"name\" in the folder \"$config_dir\")" + echo " -f, configuration file (overrides any set \"-c\" option)" + echo " -h, display this help and exit" + echo + echo "configuration keys:" + echo "emu_type: which qemu emulator to use (for example \"x86_64\" would use \"qemu-system-x86_64\")" + echo "tpm: directory of the software tpm (created automatically if specified)" + echo "memory: amount of ram for the vm" + echo "cpu_cores: amount of cpu cores for the vm" + echo "efi_files_root: base path to efi files (using \"\${efi_file_root}_OVMF_CODE.fd and \${efi_file_root}_OVMF_VARS.fd\")" + echo "disk: path to vm disk image (gets created as cow if it does not exist)" + echo "disk_size: how big the created disk is" + echo "shared_dirs: pipe separated directories that will be shared from the host to the guest" + echo "port_forwards: port forwards between guest and host (format is protocol:host_port:guest_port)" + echo "cdrom: cd image that will be mounted into the guest" +} + +parse_args() { + while getopts ":hc:f:" opt; do + case $opt in + h) + display_help + exit 0 + ;; + c) + config_file="$config_dir/$OPTARG" + ;; + f) + config_file="$OPTARG" + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + display_help + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + display_help + exit 1 + ;; + esac + done +} + +parse_args "$@" +read_config + +if [ -n "$tpm" ]; then + tpm_dir="$tmpdir/$tpm" + tpm_socket="$tpm_dir/swtpm-sock" + mkdir -p "$tpm_dir" + swtpm socket --tpmstate dir="$tpm_dir" --ctrl type=unixio,path="$tpm_socket" --log level=20 --tpm2 --daemon + tpm_args="-tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0 -chardev socket,id=chrtpm,path=$tpm_socket" +fi + +if [ -n "$efi_files_root" ]; then + efi_code_path="${efi_files_root}_OVMF_CODE.fd" + efi_vars_path="${efi_files_root}_OVMF_VARS.fd" + + if [ ! -f "$efi_code_path" ]; then + echo "$efi_code_path does not exist" + exit 1 + fi + + if [ ! -f "$efi_vars_path" ]; then + echo "$efi_vars_path does not exist" + exit 1 + fi + + efi_args="-drive if=pflash,format=raw,readonly=on,file=$efi_code_path -drive if=pflash,format=raw,file=$efi_vars_path" +fi + +if [ -n "$cdrom" ]; then + if [ ! -f "$cdrom" ]; then + echo "$cdrom does not exist" + exit 1 + fi + cdrom_args="-cdrom $cdrom" +fi + +if [ ! -f "$disk" ]; then + qemu-img create -f qcow2 "$disk" "$disk_size" +fi + +oldIFS=$IFS +IFS='|' +for dir in $shared_dirs; do + shared_dirs_args="$shared_dirs_args -net nic -net user,smb=$dir" +done +IFS=$oldIFS + +oldIFS=$IFS +IFS='|' +for port_forward in $port_forwards; do + IFS=':' read -r protocol host_port guest_port <<_EOF_ +$port_forward +_EOF_ + port_forward_args="$port_forward_args -nic user,hostfwd=$protocol::$host_port-:$guest_port" +done +IFS=$oldIFS + +# shellcheck disable=2086,2154 +qemu-system-$emu_type \ + -enable-kvm \ + -machine q35,smm=on,accel=kvm \ + -global ICH9-LPC.disable_s3=1 \ + -device intel-iommu \ + -m "$memory" \ + -smp "$cpu_cores" \ + -cpu host \ + -vga qxl \ + -device virtio-serial-pci \ + -device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0 \ + -device intel-hda \ + -device hda-duplex \ + -chardev spicevmc,id=spicechannel0,name=vdagent \ + -spice gl=on,unix=on,addr=/run/user/1000/spice.sock,disable-ticketing=on \ + -drive file="$disk",index=0,media=disk,if=virtio \ + -full-screen \ + -boot menu=off \ + -display spice-app \ + -device nec-usb-xhci,id=usb \ + -chardev spicevmc,name=usbredir,id=usbredirchardev1 \ + -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1 \ + -device qemu-xhci,id=xhci $tpm_args $efi_args $shared_dirs_args $port_forward_args $cdrom_args