feat: Implement graceful shutdown (#81)
This commit is contained in:
parent
8474b917b2
commit
08f040a819
@ -12,4 +12,4 @@ services:
|
|||||||
- 3389:3389/tcp
|
- 3389:3389/tcp
|
||||||
- 3389:3389/udp
|
- 3389:3389/udp
|
||||||
stop_grace_period: 2m
|
stop_grace_period: 2m
|
||||||
restart: unless-stopped
|
restart: on-failure
|
||||||
|
@ -39,13 +39,13 @@ services:
|
|||||||
- 3389:3389/tcp
|
- 3389:3389/tcp
|
||||||
- 3389:3389/udp
|
- 3389:3389/udp
|
||||||
stop_grace_period: 2m
|
stop_grace_period: 2m
|
||||||
restart: unless-stopped
|
restart: on-failure
|
||||||
```
|
```
|
||||||
|
|
||||||
Via `docker run`
|
Via `docker run`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -it --rm -p 8006:8006 --device=/dev/kvm --cap-add NET_ADMIN dockurr/windows
|
docker run -it --rm -p 8006:8006 --device=/dev/kvm --cap-add NET_ADMIN --stop-timeout 120 dockurr/windows
|
||||||
```
|
```
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
@ -152,7 +152,7 @@ docker run -it --rm -p 8006:8006 --device=/dev/kvm --cap-add NET_ADMIN dockurr/w
|
|||||||
VERSION: "https://example.com/win.iso"
|
VERSION: "https://example.com/win.iso"
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, you can also place a file called `custom.iso` in an empty `/storage` folder to skip the download.
|
Alternatively, you can also rename a local file to `custom.iso` and place it in an empty `/storage` folder to skip the download.
|
||||||
|
|
||||||
* ### How do I pass-through a disk?
|
* ### How do I pass-through a disk?
|
||||||
|
|
||||||
|
16
src/entry.sh
16
src/entry.sh
@ -2,23 +2,31 @@
|
|||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
APP="Windows"
|
APP="Windows"
|
||||||
export BOOT_MODE=windows
|
BOOT_MODE="windows"
|
||||||
SUPPORT="https://github.com/dockur/windows"
|
SUPPORT="https://github.com/dockur/windows"
|
||||||
|
|
||||||
cd /run
|
cd /run
|
||||||
|
|
||||||
. reset.sh # Initialize system
|
. reset.sh # Initialize system
|
||||||
. install.sh # Get bootdisk
|
. install.sh # Run installation
|
||||||
. disk.sh # Initialize disks
|
. disk.sh # Initialize disks
|
||||||
. display.sh # Initialize graphics
|
. display.sh # Initialize graphics
|
||||||
. network.sh # Initialize network
|
. network.sh # Initialize network
|
||||||
. boot.sh # Configure boot
|
. boot.sh # Configure boot
|
||||||
. proc.sh # Initialize processor
|
. proc.sh # Initialize processor
|
||||||
|
. power.sh # Configure shutdown
|
||||||
. config.sh # Configure arguments
|
. config.sh # Configure arguments
|
||||||
|
|
||||||
trap - ERR
|
trap - ERR
|
||||||
|
|
||||||
info "Booting $APP using $VERS..."
|
info "Booting $APP using $VERS..."
|
||||||
|
[[ "$DEBUG" == [Yy1]* ]] && echo "Arguments: $ARGS" && echo
|
||||||
|
|
||||||
[[ "$DEBUG" == [Yy1]* ]] && set -x
|
{ qemu-system-x86_64 ${ARGS:+ $ARGS} >"$QEMU_OUT" 2>"$QEMU_LOG"; rc=$?; } || :
|
||||||
exec qemu-system-x86_64 ${ARGS:+ $ARGS}
|
(( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15
|
||||||
|
|
||||||
|
terminal
|
||||||
|
tail -fn +0 "$QEMU_LOG" 2>/dev/null &
|
||||||
|
cat "$QEMU_TERM" 2>/dev/null & wait $! || :
|
||||||
|
|
||||||
|
sleep 1 && finish 0
|
||||||
|
@ -31,14 +31,6 @@ set -Eeuo pipefail
|
|||||||
[[ "${VERSION,,}" == "win16" ]] && VERSION="win2016-eval"
|
[[ "${VERSION,,}" == "win16" ]] && VERSION="win2016-eval"
|
||||||
[[ "${VERSION,,}" == "win2016" ]] && VERSION="win2016-eval"
|
[[ "${VERSION,,}" == "win2016" ]] && VERSION="win2016-eval"
|
||||||
|
|
||||||
if [[ "${VERSION,,}" == "tiny10" ]]; then
|
|
||||||
VERSION="https://archive.org/download/tiny-10-23-h2/tiny10%20x64%2023h2.iso"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${VERSION,,}" == "tiny11" ]]; then
|
|
||||||
VERSION="https://archive.org/download/tiny-11-core-x-64-beta-1/tiny11%20core%20x64%20beta%201.iso"
|
|
||||||
fi
|
|
||||||
|
|
||||||
CUSTOM="custom.iso"
|
CUSTOM="custom.iso"
|
||||||
|
|
||||||
[ ! -f "$STORAGE/$CUSTOM" ] && CUSTOM="Custom.iso"
|
[ ! -f "$STORAGE/$CUSTOM" ] && CUSTOM="Custom.iso"
|
||||||
@ -91,14 +83,9 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
html "$MSG"
|
html "$MSG"
|
||||||
|
|
||||||
TMP="$STORAGE/tmp"
|
TMP="$STORAGE/tmp"
|
||||||
|
[ -z "$MANUAL" ] && MANUAL="N"
|
||||||
if [ -z "$MANUAL" ]; then
|
|
||||||
|
|
||||||
MANUAL="N"
|
|
||||||
[[ "${BASE,,}" == "tiny10"* ]] && MANUAL="Y"
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f "$STORAGE/$BASE" ]; then
|
if [ -f "$STORAGE/$BASE" ]; then
|
||||||
|
|
||||||
|
151
src/power.sh
Normal file
151
src/power.sh
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
#!/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"
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
|
||||||
|
local pid
|
||||||
|
local reason=$1
|
||||||
|
|
||||||
|
if [ -f "$QEMU_PID" ]; then
|
||||||
|
|
||||||
|
pid=$(<"$QEMU_PID")
|
||||||
|
echo && error "Forcefully terminating Windows, reason: $reason..."
|
||||||
|
{ kill -15 "$pid" || true; } 2>/dev/null
|
||||||
|
|
||||||
|
while isAlive "$pid"; do
|
||||||
|
sleep 1
|
||||||
|
# Workaround for zombie pid
|
||||||
|
[ ! -f "$QEMU_PID" ] && break
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
pid="/var/run/tpm.pid"
|
||||||
|
[ -f "$pid" ] && pKill "$(<"$pid")"
|
||||||
|
|
||||||
|
closeNetwork
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
echo && echo "❯ Shutdown completed!"
|
||||||
|
|
||||||
|
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
|
||||||
|
echo && info "Received $1 while already shutting down..."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
touch "$QEMU_END"
|
||||||
|
echo && info "Received $1, sending ACPI shutdown signal..."
|
||||||
|
|
||||||
|
if [ ! -f "$QEMU_PID" ]; then
|
||||||
|
echo && error "QEMU PID file does not exist?"
|
||||||
|
finish "$code" && return "$code"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local pid=""
|
||||||
|
pid=$(<"$QEMU_PID")
|
||||||
|
|
||||||
|
if ! isAlive "$pid"; then
|
||||||
|
echo && error "QEMU process does not exist?"
|
||||||
|
finish "$code" && return "$code"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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
|
||||||
|
echo && error "Shutdown timeout reached, aborting..."
|
||||||
|
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
|
Loading…
Reference in New Issue
Block a user