mirror of
https://github.com/dockur/windows.git
synced 2026-06-09 22:17:22 +01:00
213 lines
5.0 KiB
Bash
213 lines
5.0 KiB
Bash
#!/usr/bin/env bash
|
||
set -Eeuo pipefail
|
||
|
||
: "${SHUTDOWN:="Y"}" # Graceful ACPI shutdown
|
||
: "${TIMEOUT:="115"}" # QEMU termination timeout
|
||
|
||
# Configure QEMU for graceful shutdown
|
||
|
||
QEMU_PTY="$QEMU_DIR/qemu.pty"
|
||
QEMU_END="$QEMU_DIR/qemu.end"
|
||
|
||
_trap() {
|
||
local func="$1"; shift
|
||
local sig
|
||
TRAP_PID=$BASHPID
|
||
|
||
for sig; do
|
||
trap "$func $sig" "$sig"
|
||
done
|
||
}
|
||
|
||
app() {
|
||
echo "$APP" && return 0
|
||
}
|
||
|
||
boot() {
|
||
|
||
[ -f "$QEMU_END" ] && return 0
|
||
|
||
if [ -s "$QEMU_PTY" ]; then
|
||
if [ "$(stat -c%s "$QEMU_PTY")" -gt 7 ]; then
|
||
local fail=""
|
||
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then
|
||
grep -Fq "No bootable device." "$QEMU_PTY" && fail="y"
|
||
grep -Fq "BOOTMGR is missing" "$QEMU_PTY" && fail="y"
|
||
fi
|
||
if [ -z "$fail" ]; then
|
||
info "$(app) started successfully, visit http://127.0.0.1:8006/ to view the screen..."
|
||
return 0
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
error "Timeout while waiting for QEMU to boot the machine, aborting..."
|
||
sKill "$QEMU_PID"
|
||
|
||
return 0
|
||
}
|
||
|
||
ready() {
|
||
|
||
[ -f "$STORAGE/windows.boot" ] && return 0
|
||
[ ! -s "$QEMU_PTY" ] && return 1
|
||
|
||
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then
|
||
local last
|
||
local bios="Booting from Hard"
|
||
last=$(grep "^Booting.*" "$QEMU_PTY" | tail -1)
|
||
[[ "${last,,}" != "${bios,,}"* ]] && return 1
|
||
grep -Fq "No bootable device." "$QEMU_PTY" && return 1
|
||
grep -Fq "BOOTMGR is missing" "$QEMU_PTY" && return 1
|
||
return 0
|
||
fi
|
||
|
||
local line="\"Windows Boot Manager\""
|
||
grep -Fq "$line" "$QEMU_PTY" && return 0
|
||
|
||
return 1
|
||
}
|
||
|
||
finish() {
|
||
|
||
local i=0
|
||
local pid=""
|
||
local reason=$1
|
||
local pids=( "${SMB_PID:-}" "${NMB_PID:-}" "${DDN_PID:-}" "${TPM_PID:-}" "${WSD_PID:-}" \
|
||
"${WEB_PID:-}" "${PASST_PID:-}" "${DNSMASQ_PID:-}" "${BALLOONING_PID:-}" )
|
||
|
||
touch "$QEMU_END"
|
||
|
||
if [ -s "$QEMU_PID" ]; then
|
||
if read -r pid <"$QEMU_PID"; then
|
||
if [ -n "$pid" ] && isAlive "$pid"; then
|
||
local display="$reason"
|
||
case "$reason" in
|
||
129 ) display="SIGHUP" ;;
|
||
130 ) display="SIGINT" ;;
|
||
131 ) display="SIGQUIT" ;;
|
||
134 ) display="SIGABRT" ;;
|
||
143 ) display="SIGTERM" ;;
|
||
esac
|
||
error "Forcefully terminating $(app), reason: $display..."
|
||
{ disown "$pid" || :; kill -9 -- "$pid" || :; } 2>/dev/null
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
if [ ! -f "$STORAGE/windows.boot" ] && [ -f "$BOOT" ]; then
|
||
# Remove CD-ROM ISO after install
|
||
if ready; then
|
||
local file="$STORAGE/windows.boot"
|
||
touch "$file"
|
||
! setOwner "$file" && error "Failed to set the owner for \"$file\" !"
|
||
if [[ "$REMOVE" != [Nn]* ]]; then
|
||
rm -f "$BOOT" 2>/dev/null || true
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
mKill "${pids[@]}"
|
||
closeNetwork
|
||
|
||
if ! waitPidFile "$QEMU_PID" 10; then
|
||
warn "Timed out while waiting for $(app) to exit!"
|
||
fi
|
||
|
||
(( reason != 1 )) && echo && echo "❯ Shutdown completed!"
|
||
exit "$reason"
|
||
}
|
||
|
||
graceful_shutdown() {
|
||
|
||
local sig="$1"
|
||
local pid=""
|
||
local code=0
|
||
|
||
[[ $BASHPID != "$TRAP_PID" ]] && return
|
||
|
||
case "$sig" in
|
||
SIGHUP) code=129 ;;
|
||
SIGINT) code=130 ;;
|
||
SIGQUIT) code=131 ;;
|
||
SIGABRT) code=134 ;;
|
||
SIGTERM) code=143 ;;
|
||
esac
|
||
|
||
if [ -f "$QEMU_END" ]; then
|
||
echo && info "Received $1 signal while already shutting down..."
|
||
return
|
||
fi
|
||
|
||
set +e
|
||
touch "$QEMU_END"
|
||
echo && info "Received $1 signal, sending ACPI shutdown signal..."
|
||
|
||
if [ ! -s "$QEMU_PID" ] || ! read -r pid <"$QEMU_PID"; then
|
||
warn "QEMU PID file ($QEMU_PID) does not exist?"
|
||
finish "$code"
|
||
fi
|
||
|
||
if [ -z "$pid" ] || ! isAlive "$pid"; then
|
||
warn "QEMU process with PID $pid does not exist?"
|
||
finish "$code"
|
||
fi
|
||
|
||
if ! ready; then
|
||
info "Cannot send ACPI signal during $(app) setup, aborting..."
|
||
sKill "$QEMU_PID"
|
||
if ! waitPidFile "$QEMU_PID" 5; then
|
||
warn "Timed out while waiting for $(app) to exit!"
|
||
fi
|
||
finish "$code"
|
||
fi
|
||
|
||
local cnt=0 abort=0 factor=3 offset=3 min max name
|
||
|
||
[[ "$TIMEOUT" =~ ^[0-9]+$ ]] || TIMEOUT=115
|
||
[ "$TIMEOUT" -ge 15 ] && factor=4 && offset=4
|
||
[ "$TIMEOUT" -ge 30 ] && factor=5 && offset=5
|
||
min=$((factor + offset + 1))
|
||
[ "$TIMEOUT" -lt "$min" ] && TIMEOUT="$min"
|
||
max=$(( TIMEOUT - offset ))
|
||
abort=$(( max - factor ))
|
||
name="$(app)"
|
||
|
||
while [ "$cnt" -le "$max" ]; do
|
||
|
||
sleep 1 &
|
||
local slp=$!
|
||
|
||
! isAlive "$pid" && break
|
||
# Workaround for zombie pid
|
||
[ ! -s "$QEMU_PID" ] && break
|
||
|
||
if [ "$cnt" -ne "$abort" ]; then
|
||
if [ "$cnt" -gt 0 ]; then
|
||
info "Waiting for $name to shut down... ($cnt/$max)"
|
||
fi
|
||
else
|
||
info "${name^} is still running, sending SIGTERM... ($cnt/$max)"
|
||
{ kill -15 -- "$pid" || :; } 2>/dev/null
|
||
fi
|
||
|
||
# Send ACPI shutdown signal
|
||
if [ -S "$QEMU_DIR/monitor.sock" ]; then
|
||
nc -q 1 -w 1 -U "$QEMU_DIR/monitor.sock" &> /dev/null <<<'system_powerdown' || :
|
||
fi
|
||
|
||
wait $slp
|
||
(( cnt++ ))
|
||
|
||
done
|
||
|
||
finish "$code"
|
||
}
|
||
|
||
[[ "$SHUTDOWN" != [Yy1]* ]] && return 0
|
||
[ -n "${QEMU_TIMEOUT:-}" ] && TIMEOUT="$QEMU_TIMEOUT"
|
||
|
||
_trap graceful_shutdown SIGTERM SIGHUP SIGABRT SIGQUIT
|
||
|
||
return 0
|