2024-01-23 21:38:17 +00:00
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -Eeuo pipefail
|
|
|
|
|
|
|
|
|
|
# Configure QEMU for graceful shutdown
|
|
|
|
|
|
|
|
|
|
QEMU_TERM=""
|
|
|
|
|
QEMU_PORT=7100
|
|
|
|
|
QEMU_TIMEOUT=110
|
|
|
|
|
QEMU_PID="/run/shm/qemu.pid"
|
2024-01-24 04:38:16 +00:00
|
|
|
|
QEMU_PTY="/run/shm/qemu.pty"
|
2024-01-23 21:38:17 +00:00
|
|
|
|
QEMU_LOG="/run/shm/qemu.log"
|
|
|
|
|
QEMU_OUT="/run/shm/qemu.out"
|
|
|
|
|
QEMU_END="/run/shm/qemu.end"
|
|
|
|
|
|
|
|
|
|
rm -f /run/shm/qemu.*
|
|
|
|
|
touch "$QEMU_LOG"
|
|
|
|
|
|
|
|
|
|
_trap() {
|
|
|
|
|
func="$1" ; shift
|
|
|
|
|
for sig ; do
|
|
|
|
|
trap "$func $sig" "$sig"
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 02:47:32 +00:00
|
|
|
|
ready() {
|
|
|
|
|
|
|
|
|
|
[ -f "$STORAGE/windows.boot" ] && return 0
|
|
|
|
|
[ ! -f "$QEMU_PTY" ] && return 1
|
|
|
|
|
|
|
|
|
|
if [ -f "$STORAGE/windows.old" ]; then
|
|
|
|
|
local last
|
|
|
|
|
local bios="Booting from Hard Disk"
|
|
|
|
|
last=$(grep "^B.*" "$QEMU_PTY" | tail -1)
|
|
|
|
|
if [[ "${last,,}" == "${bios,,}"* ]]; then
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
local line="Windows Boot Manager"
|
|
|
|
|
if grep -Fq "$line" "$QEMU_PTY"; then
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-23 21:38:17 +00:00
|
|
|
|
finish() {
|
|
|
|
|
|
|
|
|
|
local pid
|
|
|
|
|
local reason=$1
|
|
|
|
|
|
|
|
|
|
if [ -f "$QEMU_PID" ]; then
|
|
|
|
|
|
|
|
|
|
pid=$(<"$QEMU_PID")
|
2024-01-24 04:38:16 +00:00
|
|
|
|
error "Forcefully terminating Windows, reason: $reason..."
|
2024-01-23 21:38:17 +00:00
|
|
|
|
{ kill -15 "$pid" || true; } 2>/dev/null
|
|
|
|
|
|
|
|
|
|
while isAlive "$pid"; do
|
|
|
|
|
sleep 1
|
|
|
|
|
# Workaround for zombie pid
|
|
|
|
|
[ ! -f "$QEMU_PID" ] && break
|
|
|
|
|
done
|
|
|
|
|
fi
|
|
|
|
|
|
2024-02-06 02:47:32 +00:00
|
|
|
|
if [ ! -f "$STORAGE/windows.boot" ] && [ -f "$STORAGE/$BASE" ]; then
|
|
|
|
|
# Remove CD-ROM ISO after install
|
|
|
|
|
if ready; then
|
|
|
|
|
rm -f "$STORAGE/$BASE"
|
|
|
|
|
touch "$STORAGE/windows.boot"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
2024-01-23 21:38:17 +00:00
|
|
|
|
pid="/var/run/tpm.pid"
|
|
|
|
|
[ -f "$pid" ] && pKill "$(<"$pid")"
|
|
|
|
|
|
|
|
|
|
closeNetwork
|
|
|
|
|
|
2024-01-24 04:38:16 +00:00
|
|
|
|
sleep 0.5
|
|
|
|
|
echo "❯ Shutdown completed!"
|
2024-01-23 21:38:17 +00:00
|
|
|
|
|
|
|
|
|
exit "$reason"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
terminal() {
|
|
|
|
|
|
|
|
|
|
local dev=""
|
|
|
|
|
|
|
|
|
|
if [ -f "$QEMU_OUT" ]; then
|
|
|
|
|
|
|
|
|
|
local msg
|
|
|
|
|
msg=$(<"$QEMU_OUT")
|
|
|
|
|
|
|
|
|
|
if [ -n "$msg" ]; then
|
|
|
|
|
|
|
|
|
|
if [[ "${msg,,}" != "char"* || "$msg" != *"serial0)" ]]; then
|
|
|
|
|
echo "$msg"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
dev="${msg#*/dev/p}"
|
|
|
|
|
dev="/dev/p${dev%% *}"
|
|
|
|
|
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [ ! -c "$dev" ]; then
|
|
|
|
|
dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$QEMU_PORT" | tr -d '\000')
|
|
|
|
|
dev="${dev#*serial0}"
|
|
|
|
|
dev="${dev#*pty:}"
|
|
|
|
|
dev="${dev%%$'\n'*}"
|
|
|
|
|
dev="${dev%%$'\r'*}"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [ ! -c "$dev" ]; then
|
|
|
|
|
error "Device '$dev' not found!"
|
|
|
|
|
finish 34 && return 34
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
QEMU_TERM="$dev"
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_graceful_shutdown() {
|
|
|
|
|
|
|
|
|
|
local code=$?
|
|
|
|
|
|
|
|
|
|
set +e
|
|
|
|
|
|
|
|
|
|
if [ -f "$QEMU_END" ]; then
|
2024-01-24 04:38:16 +00:00
|
|
|
|
info "Received $1 while already shutting down..."
|
2024-01-23 21:38:17 +00:00
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
touch "$QEMU_END"
|
2024-01-24 04:38:16 +00:00
|
|
|
|
info "Received $1, sending ACPI shutdown signal..."
|
2024-01-23 21:38:17 +00:00
|
|
|
|
|
|
|
|
|
if [ ! -f "$QEMU_PID" ]; then
|
2024-01-24 04:38:16 +00:00
|
|
|
|
error "QEMU PID file does not exist?"
|
2024-01-23 21:38:17 +00:00
|
|
|
|
finish "$code" && return "$code"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
local pid=""
|
|
|
|
|
pid=$(<"$QEMU_PID")
|
|
|
|
|
|
|
|
|
|
if ! isAlive "$pid"; then
|
2024-01-24 04:38:16 +00:00
|
|
|
|
error "QEMU process does not exist?"
|
2024-01-23 21:38:17 +00:00
|
|
|
|
finish "$code" && return "$code"
|
|
|
|
|
fi
|
|
|
|
|
|
2024-02-06 02:47:32 +00:00
|
|
|
|
if ! ready; then
|
|
|
|
|
info "Cannot send ACPI signal during Windows setup, aborting..."
|
|
|
|
|
finish "$code" && return "$code"
|
2024-01-24 04:38:16 +00:00
|
|
|
|
fi
|
|
|
|
|
|
2024-01-23 21:38:17 +00:00
|
|
|
|
# Send ACPI shutdown signal
|
|
|
|
|
echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_PORT}" > /dev/null
|
|
|
|
|
|
|
|
|
|
local cnt=0
|
|
|
|
|
while [ "$cnt" -lt "$QEMU_TIMEOUT" ]; do
|
|
|
|
|
|
|
|
|
|
sleep 1
|
|
|
|
|
cnt=$((cnt+1))
|
|
|
|
|
|
|
|
|
|
! isAlive "$pid" && break
|
|
|
|
|
# Workaround for zombie pid
|
|
|
|
|
[ ! -f "$QEMU_PID" ] && break
|
|
|
|
|
|
|
|
|
|
info "Waiting for Windows to shutdown... ($cnt/$QEMU_TIMEOUT)"
|
|
|
|
|
|
|
|
|
|
# Send ACPI shutdown signal
|
|
|
|
|
echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_PORT}" > /dev/null
|
|
|
|
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
if [ "$cnt" -ge "$QEMU_TIMEOUT" ]; then
|
2024-01-24 04:38:16 +00:00
|
|
|
|
error "Shutdown timeout reached, aborting..."
|
2024-01-23 21:38:17 +00:00
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
finish "$code" && return "$code"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SERIAL="pty"
|
|
|
|
|
MONITOR="telnet:localhost:$QEMU_PORT,server,nowait,nodelay"
|
|
|
|
|
MONITOR="$MONITOR -daemonize -D $QEMU_LOG -pidfile $QEMU_PID"
|
|
|
|
|
|
|
|
|
|
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT
|
|
|
|
|
|
|
|
|
|
return 0
|