Files
dockur-windows/src/power.sh
T
2026-06-05 03:14:50 +02:00

213 lines
5.0 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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