#!/usr/local/bin/bash # set -euo pipefail IFS=$'\n\t' DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" : ${CD:=""} : ${VNC_ENABLE:="NO"} : ${VNC_LISTEN:="127.0.0.1:5900"} : ${PID_FILE:="/var/run/unifi.pid"} ############## Setup ######################### function cleanup { for vm in "${vms[@]}"; do log "Destroying bhyve vm $vm" bhyvectl "--vm=$vm" --destroy log "Destroyed bhyve vm $vm" done } vms=() for sig in EXIT INT QUIT HUP TERM; do trap "set +e; sleep 10; cleanup" "$sig" done function die { local status_code="$1" shift (>&2 echo "${@}") exit "$status_code" } function log { (>&2 echo "${@}") } ############## Program ######################### function main { start_vm } function start_vm { local name="unifi" # -H release the CPU when guest issues HLT instruction. Otherwise 100% of core will be consumed. # -s 3,ahci-cd,/vm/.iso/archlinux-2023.04.01-x86_64.iso \ # -s 29,fbuf,tcp=0.0.0.0:5900,w=1920,h=1080,wait \ # -s 29,fbuf,tcp=0.0.0.0:5900,w=1920,h=1080 \ # TODO: Look into using nmdm instead of stdio for serial console if [ -n "$CD" ]; then additional_args+=("-s" "5,ahci-cd,$CD") fi if [ "$VNC_ENABLE" = "YES" ]; then additional_args+=("-s" "29,fbuf,tcp=$VNC_LISTEN,w=1920,h=1080") fi local bridge_name="bridge_vm" wait_for_bridge "$bridge_name" local mac_address mac_address=$(calculate_mac_address "$name") local bridge_link_name bridge_link_name=$(detect_available_link "${bridge_name}") additional_args+=("-s" "2:0,virtio-net,netgraph,path=${bridge_name}:,peerhook=${bridge_link_name},mac=${mac_address}") vms+=("$name") while true; do set -x set +e bhyve \ -D \ -c 1 \ -m 3G \ -H \ -o 'rtc.use_localtime=false' \ -s 0,hostbridge \ -s "4,nvme,/dev/zvol/zroot/vm/unifi/disk0" \ -s 30,xhci,tablet \ -s 31,lpc -l com1,stdio \ -l "bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd,/vm/unifi/BHYVE_UEFI_VARS.fd" \ "${additional_args[@]}" \ "$name" # local bhyvepid=$! # echo "$bhyvepid" > "$PID_FILE" # wait $bhyvepid local exit_code=$? set +x set -e if [ $exit_code -eq 0 ]; then echo "Rebooting." sleep 5 elif [ $exit_code -eq 1 ]; then echo "Powered off." break elif [ $exit_code -eq 2 ]; then echo "Halted." break elif [ $exit_code -eq 3 ]; then echo "Triple fault." break elif [ $exit_code -eq 4 ]; then echo "Exited due to an error." break fi done } function ng_exists { ngctl status "${1}" >/dev/null 2>&1 } function wait_for_bridge { local bridge_name="$1" while ! ng_exists "${bridge_name}:"; do echo "${bridge_name} does not yet exist, sleeping." sleep 10 done } function detect_available_link { local bridge_name="$1" local linknum=1 while true; do local link_name="link${linknum}" if ! ng_exists "${bridge_name}:${link_name}"; then echo "$link_name" return fi linknum=$((linknum + 1)) if [ "$linknum" -gt 90 ]; then (>&2 echo "No available links on bridge $bridge_name") exit 1 fi done } function calculate_mac_address { local name="$1" local source source=$(md5 -r -s "$name" | awk '{print $1}') echo "06:${source:0:2}:${source:2:2}:${source:4:2}:${source:6:2}:${source:8:2}" } main "${@}"