#!/usr/bin/bash
# create and run clone

set -eu

show_help() {
    cat << EOF >&2
Usage: $(basename "$0") [options] vmname"
Create a new clone, start the vm (if not yet running) and run virt-viewer.
Squid-Proxy will be started too.
options:
    -n|--new             new clone will be created, even if exists
    -p|--persistent      new clone will be created persistent, so available after reboot too
    -s|--system          qemu:///system instead of default qemu:///session
       --no-viewer       start without viewer
       --heads n         number of displays
       --memory sizeMB   memory size in MB
       --cpu num         number of CPUs
       --os OS           operating system (win10|linux|..)
       --data-disk size  additional data-disk
       --bridge virbrX   additional network interface on bridge virbrX
       --uid uid         set uid on guest
       --gid gid         set gid on guest
       --macvtap         additional network interface on device macvtap
       --options options additional options for virt-install command
EOF
}

exit_script() {
    echo "run-vm.sh ${VM_NAME} terminated by trap!" >> "/tmp/${UID}/exit-run-vm.log"
    virsh --connect="${QEMU}" destroy "${VM_NAME}-clone"
    trap - SIGHUP SIGINT SIGTERM # clear the trap
    kill -- -$$ # Sends SIGTERM to child/sub processes
}

check_images() {
   # sync vm-torrents and machine definition file
   sudo -u lmnsynci /usr/local/bin/vm-sync get_file "${VM_NAME}.qcow2.torrent"
   [[ -f "${VM_SYSDIR}/${VM_NAME}.qcow2" ]] && sudo -u lmnsynci /usr/local/bin/vm-sync delete_outdated_image "${VM_NAME}.qcow2"

   BACKINGARRAY=()
   imgfile="${VM_SYSDIR}/${VM_NAME}.qcow2" && [[ -f "${VM_DIR}/${VM_NAME}.qcow2" ]] && imgfile="${VM_DIR}/${VM_NAME}.qcow2"
   BACKINGARRAY+=("${imgfile}")
   echo "Imgfile=$imgfile"
   if [[ ! -f "${imgfile}" ]] || ! qemu-img info -U "${imgfile}" | grep "file format: qcow2"; then
       if [[ ! -f "${VM_SYSDIR}/${VM_NAME}.qcow2.torrent" ]]; then
           echo "no base VM disk '${VM_NAME}.qcow2' found and/or ${VM_NAME} not found on server" >&2
           exit 1
       fi
       # sync vm-disk image by torrent
       echo "Try to sync VM ${VM_NAME} by torrent"
       sudo -u lmnsynci /usr/local/bin/vm-sync get_image "${VM_NAME}"
   fi

   backingfile=$(qemu-img info -U "${imgfile}" | grep "^backing file:" | cut -d ' ' -f 3)
   while [[ -n "${backingfile}" ]]; do
       echo "Backingfile required: ${backingfile}"
       imgfile="${VM_SYSDIR}/${backingfile}" && [[ -f "${VM_DIR}/${backingfile}" ]] && imgfile="${VM_DIR}/${backingfile}"
       BACKINGARRAY+=("${imgfile}")
       sudo -u lmnsynci /usr/local/bin/vm-sync get_file "${backingfile}.torrent"
       [[ -f "${VM_SYSDIR}/${backingfile}" ]] && sudo -u lmnsynci /usr/local/bin/vm-sync delete_outdated_image "${backingfile}"
       if [[ ! -f "${imgfile}" ]] || ! qemu-img info -U "${imgfile}" | grep "file format: qcow2"; then
           # sync vm-disk image by torrent
           echo "Try to sync backingfile ${backingfile} by torrent"
           sudo -u lmnsynci /usr/local/bin/vm-sync get_image "${backingfile%.qcow2}"
       fi
       backingfile=$(qemu-img info -U "${imgfile}" | grep "^backing file:" | cut -d ' ' -f 3)
   done

   echo "VM-Image and required backingfiles available"
   echo "Now, let's check the images."

   # Check VM-Images in reverse order
   for ((i=${#BACKINGARRAY[@]}-1; i>=0; i--))
   do
       echo "Checking ${BACKINGARRAY[$i]}"
       if ! qemu-img check -U "${BACKINGARRAY[$i]}" 2>/dev/null; then
           echo "check failed!"
           echo "sync ${BACKINGARRAY[$i]} again"
           sudo -u lmnsynci /usr/local/bin/vm-sync get_file "${BACKINGARRAY[$i]}.torrent"
           sudo -u lmnsynci /usr/local/bin/vm-sync get_image "$(basename "${BACKINGARRAY[$i]}" .qcow2)"
       fi
   done
   echo "VM-Image and required backingfiles available and checked"

   sudo -u lmnsynci /usr/local/bin/vm-sync update_usage_information ${BACKINGARRAY[*]}
}

create_clone() {
    local VM_NAME="$1"

    if ! [[ -f "${VM_SYSDIR}/${VM_NAME}.qcow2" || -f "${VM_DIR}/${VM_NAME}.qcow2" ]]; then
	echo "qcow2 File does not exists." >&2
	exit 1
    fi

    # Create User-VM-Dir and link system VM-Images
    [[ -d "${VM_DIR}" ]] || mkdir -p "${VM_DIR}"
    if [[ "${PERSISTENT}" -eq 1 ]]; then
        sudo /usr/local/bin/vm-link-images -p
    else
        sudo /usr/local/bin/vm-link-images
    fi

    # Create backing file
    cd "${VM_DIR}"
    qemu-img create -f qcow2 -F qcow2 -b "${VM_NAME}.qcow2" "${VM_NAME}-clone.qcow2"

}

create_printerlist() {
  ## Prepare .printerlist.csv
  mkdir -p "${VM_MEDIADIR}"
  chgrp "$(id -g)" "${VM_MEDIADIR}"
  echo "Name;IppURL" > "${VM_MEDIADIR}/.printerlist.csv"
  for p in $(lpstat -v | cut -f 3 -d" " | sed 's/:$//'); do
    echo "$p;ipp://192.168.122.1/printers/$p" >> "${VM_MEDIADIR}/.printerlist.csv"
  done
}

start_virtiofsd() {
  # BEGIN temporary fix, while linux-starter are not migrated to --uid and --gid
  if [[ "$LIBVIRTOSINFO" =~ debian.* ]]; then
    [[ "$GUEST_UID" == 0 ]] && GUEST_UID=1010
    [[ "$GUEST_GID" == 0 ]] && GUEST_GID=1010
  fi
  # END temporary fix
  socket="/run/user/$(id -u $USER)/virtiofs-${VM_NAME}.sock"
  /usr/local/bin/virtiofsd --uid-map=:${GUEST_UID}:${UID}:1: --gid-map=:${GUEST_GID}:$(id -g):1: \
	  --socket-path "$socket" --shared-dir "/lmn/media/${USER}" --syslog &
}

QEMU='qemu:///session'

NEWCLONE=0
PERSISTENT=0
LIBVIRTOSINFO="win10"
LIBVIRTOPTS=""
NO_VIEWER=0
GUEST_UID=0
GUEST_GID=0

source /etc/lmn/vm.conf

TEMP=$(getopt -o no:ps --long new,no-viewer,options:,persistent,system,memory:,data-disk:,heads:,cpu:,bridge:,macvtap,os:,uid:,gid:,help -n $0 -- "$@")
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

eval set -- "$TEMP"

while true; do
  case "$1" in
    -p | --persistent )
	    PERSISTENT=1;
            VM_DIR="${VM_DIR_PERSISTENT}"
	    shift
	    ;;
    -n | --new )
            NEWCLONE=1
	    shift
	    ;;
    -s | --system )
            QEMU='qemu:///system'
	    shift
	    ;;
    -o | --options )
            LIBVIRTOPTS=$2
	    shift 2
	    ;;
    --no-viewer )
            NO_VIEWER=1
	    shift
	    ;;
    --data-disk )
            LIBVIRTOPTS="${LIBVIRTOPTS}  --disk ${VM_DIR}/data.qcow2,size=$2,sparse=yes"
	    shift 2
	    ;;
    --heads )
            for i in $(seq $2)
            do
              LIBVIRTOPTS="${LIBVIRTOPTS}  --video model.heads=$i"
            done
            shift 2
            ;;
    --memory )
	    mem_avail=$(sed -En "s/^MemAvailable:\s+([0-9]+)\s+kB/\1/p" /proc/meminfo)
	    mem_avail=$((mem_avail / 1024 - 2048))
	    if (( $2 < mem_avail )); then
	      LIBVIRTOPTS="${LIBVIRTOPTS} --memory $2"
	    else
	      LIBVIRTOPTS="${LIBVIRTOPTS} --memory ${mem_avail}"
	    fi
	    shift 2
	    ;;
    --cpu )
	    #cpu=$(sed -En "0,/^cpu cores/s/^cpu cores\s+:\s+([0-9]+)/\1/p" /proc/cpuinfo)
	    cpu=$(lscpu | grep "^CPU(s):" | sed 's/.* //g')
	    if (( $2 < cpu )); then
	      LIBVIRTOPTS="${LIBVIRTOPTS} --vcpu $2"
	    else
	      LIBVIRTOPTS="${LIBVIRTOPTS} --vcpu ${cpu}"
	    fi
	    shift 2
	    ;;
    --bridge )
            if ip link | grep $2; then
               LIBVIRTOPTS="${LIBVIRTOPTS} --network=bridge=$2,model.type=virtio"
            fi
	    shift 2
	    ;;
    --macvtap )
	    for interface in $(ip link | sed -En 's/.*(macvtap-.*)@.*/\1/p'); do
              mac="$(ip link | grep -A1 "${interface}" | \
                sed -nE "s%\s+link/ether ([[:xdigit:]:]{17}) .+%\1%p")"
              type="ethernet,mac=${mac},target.dev=${interface},xpath1.set=./target/@managed=no,model.type=virtio"
	      LIBVIRTOPTS="${LIBVIRTOPTS} --network type=$type"
	    done
	    shift
	    ;;
    --os )
            LIBVIRTOSINFO=$2
	    shift 2
	    ;;
    --uid )
            GUEST_UID=$2
	    shift 2
	    ;;
    --gid )
            GUEST_GID=$2
	    shift 2
	    ;;
    --help )
            show_help
            exit 1
            ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

# if less than one arguments supplied, display usage
if [[  $# -ne 1 ]] ; then
    show_help
    exit 1
fi

VM_NAME=$1

# check, if we have to start squid
if ! killall -s 0 squid; then
    echo "starting squid."
    /usr/sbin/squid -f /etc/squid/squid-usermode.conf
fi

# because virsh has problems with long pathnames, using diffent configdir
export XDG_CONFIG_HOME="/tmp/${UID}/.config"

if  ! virsh --connect="${QEMU}" list | grep "${VM_NAME}-clone"; then
    echo "VM not yet running."
    # only when school-network is reachable
    if nslookup "${SEEDBOX_HOST}"; then
      sudo /usr/local/bin/desktop-sync
      check_images
    fi
    if [[ "${NEWCLONE}" = 1 ]] || [[ ! -f "${VM_DIR}/${VM_NAME}-clone.qcow2" ]]; then
        create_clone "${VM_NAME}"
    fi
    # delete the old vm
    virsh --connect=qemu:///session undefine "${VM_NAME}-clone" || echo "${VM_NAME}-clone did not exist"
    #trap exit_script SIGHUP SIGINT SIGTERM

    create_printerlist
    # start virtiofsd-service
    [[ "${QEMU}" = 'qemu:///session' ]] && start_virtiofsd

    # finally, create the new vm

    virt-install \
             --osinfo "${LIBVIRTOSINFO}" \
	     --name "${VM_NAME}-clone" \
	     --import \
             --clock hpet_present=yes,hypervclock_present=yes \
             --features hyperv.synic.state=on,xpath1.set=./hyperv/vpindex/@state=on,xpath2.set=./hyperv/stimer/@state=on \
	     --memorybacking source.type=memfd,access.mode=shared \
	     --disk "${VM_DIR}/${VM_NAME}-clone.qcow2",driver.discard=unmap,target.bus=scsi,cache=writeback \
	     --network=bridge=virbr0,model.type=virtio \
             --filesystem driver.type=virtiofs,accessmode=passthrough,target.dir=virtiofs,xpath1.set=./source/@socket="/run/user/${UID}/virtiofs-${VM_NAME}.sock" \
	     --controller type=scsi,model=virtio-scsi \
	     --check path_in_use=off \
             --connect="${QEMU}" \
	     --noautoconsole \
	     ${LIBVIRTOPTS}

#	     --dry-run \
#             --print-xml \
#	     > /tmp/vm.xml
#	     --features hyperv.synic.state=on,xpath1.set=./hyperv/vpindex/@state=on,xpath2.set=./hyperv/stimer/@state=on \
#	     --network type=ethernet,target.dev=vm-macvtap,xpath1.set=./target/@managed=no \
#    virsh --connect="${QEMU}" start "${VM_NAME}-clone"
fi
if [[ $NO_VIEWER == 0 ]] ; then
  echo "starting viewer"
  trap exit_script SIGHUP SIGINT SIGTERM
  virt-viewer --connect="${QEMU}" --full-screen "${VM_NAME}-clone"
fi
