#!/bin/bash

## Copyright (C) 2019 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

## Keep the command trace: it is this script's diagnostic output.
set -x
set -o errexit
set -o nounset
set -o pipefail
set -o errtrace
shopt -s inherit_errexit
shopt -s shift_verbose

## {{ Taken from qemu-system-common.postinst.
# Add the kvm group unless it's already there
if ! getent group kvm >/dev/null; then
   addgroup --quiet --system kvm || true
fi
## }} Taken from qemu-system-common.postinst.

## {{ Taken from libvirt-bin.postinst.
if ! getent group libvirt >/dev/null; then
   addgroup --system libvirt
fi
## }} Taken from libvirt-bin.postinst.

## Existence of account "user" is not guaranteed at this point.
## XXX: Or is it?
adduser user kvm >/dev/null || true
adduser user libvirt >/dev/null || true

## Create shared directory and adjust permissions
mkdir --parents /mnt/gateway-shared
mkdir --parents /mnt/workstation-shared
chmod 777 /mnt/gateway-shared
chmod 777 /mnt/workstation-shared

## Bring libvirt networks to defined + autostart + running WITHOUT masking real
## failures: each step is guarded by the network's current state, so a re-run
## over an already-defined / already-active network is a clean no-op, while a
## genuine virsh error aborts the postinst (errexit) instead of being swallowed
## by a '|| true'.

virsh_session() {
   virsh -c qemu:///session "$@"
}

## Exact-match a network name against virsh's list (all networks, or active).
net_defined() {
   virsh_session net-list --all --name | grep -Fxq -- "${1}"
}
net_active() {
   virsh_session net-list --name | grep -Fxq -- "${1}"
}

## Autostart (idempotent) then start (only when not already active) a network
## that already exists.
net_enable() {
   local name
   name="${1}"
   virsh_session net-autostart "${name}"
   if ! net_active "${name}"; then
      virsh_session net-start "${name}"
   fi
}

## Define (only when absent) then enable a network this package ships.
net_define_and_enable() {
   local name xml
   name="${1}"
   xml="${2}"
   if ! net_defined "${name}"; then
      virsh_session net-define "${xml}"
   fi
   net_enable "${name}"
}

## The 'default' network is provided by the environment, not this package, so it
## is optional: configure it only when present.
if net_defined "default"; then
   net_enable "default"
fi

net_define_and_enable "Whonix-External" "/usr/share/libvirt-dist/xml/Whonix-External.xml"
net_define_and_enable "Whonix-Internal" "/usr/share/libvirt-dist/xml/Whonix-Internal.xml"

## Doing the following in a temporary directory to avoid modified files should
## this be interrupted in the middle.
temp_dir="$(mktemp --directory)"
cp -r /usr/share/libvirt-dist/xml "${temp_dir}"

if virsh capabilities | grep "<domain type='kvm'>" ; then
   true "OK: found KVM"
else
   ## replace the 'kvm' domain type with 'qemu'
   search="<domain type='kvm'>"
   replace="<domain type='qemu'>"
   str_replace "${search}" "${replace}" "${temp_dir}/xml/Whonix-Gateway.xml"
   str_replace "${search}" "${replace}" "${temp_dir}/xml/Whonix-Workstation.xml"

   search="<cpu mode='host-passthrough'/>"
   replace=""
   str_replace "${search}" "${replace}" "${temp_dir}/xml/Whonix-Gateway.xml"
   str_replace "${search}" "${replace}" "${temp_dir}/xml/Whonix-Workstation.xml"

   ## https://forums.whonix.org/t/whonix-host-operating-system/3931/251
   search="<pvspinlock state='on'/>"
   replace=""
   str_replace "${search}" "${replace}" "${temp_dir}/xml/Whonix-Gateway.xml"
   str_replace "${search}" "${replace}" "${temp_dir}/xml/Whonix-Workstation.xml"

   ## https://forums.whonix.org/t/whonix-host-operating-system/3931/284
   search="<vcpu placement='static' cpuset='0'>1</vcpu>"
   replace=""
   str_replace "${search}" "${replace}" "${temp_dir}/xml/Whonix-Gateway.xml"

   ## https://forums.whonix.org/t/whonix-host-operating-system/3931/284
   search="<vcpu placement='static' cpuset='1'>1</vcpu>"
   replace=""
   str_replace "${search}" "${replace}" "${temp_dir}/xml/Whonix-Workstation.xml"
fi

test -f "${temp_dir}/xml/Whonix-Gateway.xml"
test -f "${temp_dir}/xml/Whonix-Workstation.xml"

## 'define' redefines an already-defined domain and returns success, so it is
## idempotent on its own; dropping '|| true' means a real failure (malformed XML
## from the substitutions above) aborts the postinst instead of being hidden.
virsh_session define "${temp_dir}/xml/Whonix-Gateway.xml"
virsh_session define "${temp_dir}/xml/Whonix-Workstation.xml"

## virt-xml --add-device appends UNCONDITIONALLY, so a reinstall would add a
## duplicate shared-filesystem device. Guard it: add only when the domain's
## persistent config has no filesystem with this source directory. A genuine
## virt-xml failure now aborts (errexit) instead of being swallowed by '|| true'.
domain_has_filesystem_source() {
   virsh_session dumpxml --inactive "${1}" | grep -Fq "dir='${2}'"
}
add_shared_filesystem() {
   local domain source
   domain="${1}"
   source="${2}"
   if ! domain_has_filesystem_source "${domain}" "${source}"; then
      virt-xml --connect qemu:///session "${domain}" --add-device --filesystem "source=${source},target=shared,type=mount,accessmode=mapped"
   fi
}

add_shared_filesystem "Whonix-Gateway" "/mnt/gateway-shared"
add_shared_filesystem "Whonix-Workstation" "/mnt/workstation-shared"

mkdir --parents /var/lib/libvirt-dist
touch /var/lib/libvirt-dist/install.done
