227 lines
4.8 KiB
Bash
Raw Normal View History

2025-08-23 20:20:06 -04:00
#!/usr/bin/env bash
#
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Share a host directory to the guest via 9pfs.
#
# Inside the VM run:
# mount -t virtfs -o trans=virtio sharename /some/vm/path
# mount -t 9p -o cache=mmap -o msize=512000 sharename /mnt/9p
# mount -t 9p -o trans=virtio,cache=mmap,msize=512000 sharename /path/to/mountpoint
# bhyve_options="-s 28,virtio-9p,sharename=/"
# Enable Sound
# bhyve_options="-s 16,hda,play=/dev/dsp,rec=/dev/dsp"
# Example usage:
#
# doas bhyverc create-disk zdata/vm/poudriere /vm/poudriere 10
# doas bhyverc start poudriere zdata/vm/poudriere /vm/poudriere /vm/iso/FreeBSD-13.2-RELEASE-amd64-bootonly.iso
# doas bhyverc start poudriere zdata/vm/poudriere /vm/poudriere
: ${VERBOSE:="NO"} # or YES
if [ "$VERBOSE" = "YES" ]; then
set -x
fi
: ${CPU_CORES:="1"}
: ${MEMORY:="1G"}
: ${NETWORK:="NAT"} # or RAW or BOTH
: ${IP_RANGE:="10.215.1.1/24"} # Ignored for RAW networks
: ${INTERFACE_NAME:="jail_nat"} # or the external interface like lagg0 for RAW networks
: ${BRIDGE_NAME:="bridge_$INTERFACE_NAME"} # or bridge_raw for RAW networks
: ${VNC_ENABLE:="NO"}
: ${VNC_LISTEN:="127.0.0.1:5900"}
: ${VNC_WIDTH:="1920"}
: ${VNC_HEIGHT:="1080"}
############## Setup #########################
function die {
local status_code="$1"
shift
(>&2 echo "${@}")
exit "$status_code"
}
function log {
(>&2 echo "${@}")
}
############## Program #########################
function main {
local cmd
cmd=$1
shift
if [ "$cmd" = "start" ]; then
init
start "${@}"
elif [ "$cmd" = "stop" ]; then
init
stop "${@}"
elif [ "$cmd" = "status" ]; then
init
status "${@}"
elif [ "$cmd" = "console" ]; then
init
console "${@}"
elif [ "$cmd" = "_start_body" ]; then
init
start_body "${@}"
else
(>&2 echo "Unknown command: $cmd")
exit 1
fi
}
function start {
local num_vms="$#"
if [ "$num_vms" -gt 0 ]; then
for name in "$@"; do
log "Starting VM $name."
start_one "$name"
done
else
log "No VMs specified."
fi
}
function start_one {
local name="$1"
local tmux_name="$name"
/usr/local/bin/tmux new-session -d -s "$tmux_name" "$0" "_start_body" "$name"
# /usr/local/bin/tmux new-session -d -s "$tmux_name" "/usr/bin/env VNC_ENABLE=NO VNC_LISTEN=0.0.0.0:5900 /usr/local/bin/bash /home/talexander/launch_opnsense.bash"
}
function start_body {
local name="$1"
local pidfile="/run/bhyverc/${name}/pid"
mkdir -p "/run/bhyverc/${name}"
trap "set +e; stop_one '${name}'" EXIT
local launch_cmd=()
launch_cmd+=(launch_pidfile "$pidfile" sleep 999)
(
IFS=$' \n\t'
bash -c "${launch_cmd[*]}"
)
}
function launch_pidfile {
local pidfile="$1"
shift 1
cat > "${pidfile}" <<< "$$"
set -x
exec "${@}"
}
export -f launch_pidfile
function stop {
local num_vms="$#"
if [ "$num_vms" -gt 0 ]; then
for name in "$@"; do
log "Stopping VM $name."
stop_one "$name"
done
else
log "No VMs specified."
fi
}
function stop_one {
local name="$1"
local pidfile="/run/bhyverc/${name}/pid"
if [ ! -e "$pidfile" ]; then
log "Pid file $pidfile does not exist."
return 0
fi
local bhyve_pid
bhyve_pid=$(cat "$pidfile")
if ps -p "$bhyve_pid" >/dev/null; then
# Send ACPI shutdown command
log "Sending ACPI shutdown to $bhyve_pid."
kill -SIGTERM "$bhyve_pid"
fi
while ps -p "$bhyve_pid" >/dev/null; do
log "Waiting for $bhyve_pid to exit."
sleep 2
done
bhyvectl "--vm=$name" --destroy || true
rm -f "$pidfile"
log "Finished stopping $name."
}
function status {
local num_vms="$#"
if [ "$num_vms" -gt 0 ]; then
for name in "$@"; do
status_one "$name"
done
else
log "No VMs specified."
fi
}
function status_one {
local name="$1"
local pidfile="/run/bhyverc/${name}/pid"
if [ ! -e "$pidfile" ]; then
log "$name is not running."
return 0
fi
local bhyve_pid
bhyve_pid=$(cat "$pidfile")
if ! ps -p "$bhyve_pid" >/dev/null; then
log "$name is not running."
return 0
fi
log "$name is running as pid $bhyve_pid."
}
function console {
local num_vms="$#"
if [ "$num_vms" -gt 0 ]; then
for name in "$@"; do
log "Attaching to console of VM $name."
console_one "$name"
done
else
log "No VMs specified."
fi
}
function console_one {
local name="$1"
local tmux_name="$name"
exec tmux a -t "$tmux_name"
}
function init {
mkdir -p /run/bhyverc
}
############## Bhyve ###########################
main "${@}"