mirror of
https://github.com/dockur/windows.git
synced 2026-08-03 12:37:20 +01:00
fix: Prevent races during interactive shutdown (#1907)
This commit is contained in:
+90
-20
@@ -13,6 +13,7 @@ QEMU_PTY="$QEMU_DIR/qemu.pty"
|
|||||||
QEMU_END="$QEMU_DIR/qemu.end"
|
QEMU_END="$QEMU_DIR/qemu.end"
|
||||||
CONSOLE_PID="$QEMU_DIR/console.pid"
|
CONSOLE_PID="$QEMU_DIR/console.pid"
|
||||||
CONSOLE_SOCKET="$QEMU_DIR/console.sock"
|
CONSOLE_SOCKET="$QEMU_DIR/console.sock"
|
||||||
|
QEMU_START_PID="$QEMU_DIR/qemu.start.pid"
|
||||||
|
|
||||||
_trap() {
|
_trap() {
|
||||||
|
|
||||||
@@ -65,11 +66,58 @@ displayReason() {
|
|||||||
readQemuPid() {
|
readQemuPid() {
|
||||||
|
|
||||||
local -n _pid="$1"
|
local -n _pid="$1"
|
||||||
|
local file
|
||||||
|
|
||||||
if [ ! -s "$QEMU_PID" ] || ! read -r _pid <"$QEMU_PID"; then
|
for file in "$QEMU_START_PID" "$QEMU_PID"; do
|
||||||
return 1
|
if [ -s "$file" ] && read -r _pid < "$file"; then
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
qemuPidFile() {
|
||||||
|
|
||||||
|
local -n _file="$1"
|
||||||
|
|
||||||
|
_file="$QEMU_PID"
|
||||||
|
[ -s "$QEMU_START_PID" ] && _file="$QEMU_START_PID"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
terminateQemu() {
|
||||||
|
|
||||||
|
local file=""
|
||||||
|
|
||||||
|
qemuPidFile file
|
||||||
|
sKill "$file"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
waitQemuExit() {
|
||||||
|
|
||||||
|
local timeout="${1:-10}"
|
||||||
|
local file=""
|
||||||
|
|
||||||
|
qemuPidFile file
|
||||||
|
waitPidFile "$file" "$timeout"
|
||||||
|
}
|
||||||
|
|
||||||
|
waitQemuPid() {
|
||||||
|
|
||||||
|
local -n _pid="$1"
|
||||||
|
local cnt=0 value=""
|
||||||
|
|
||||||
|
while ! readQemuPid value; do
|
||||||
|
sleep 0.02
|
||||||
|
cnt=$((cnt + 1))
|
||||||
|
(( cnt >= 50 )) && return 1
|
||||||
|
done
|
||||||
|
|
||||||
|
_pid="$value"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +147,7 @@ boot() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
error "Timeout while waiting for QEMU to boot the machine, aborting..."
|
error "Timeout while waiting for QEMU to boot the machine, aborting..."
|
||||||
sKill "$QEMU_PID"
|
terminateQemu
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -136,8 +184,7 @@ ready() {
|
|||||||
forceKillQemu() {
|
forceKillQemu() {
|
||||||
|
|
||||||
local reason="$1"
|
local reason="$1"
|
||||||
local pid=""
|
local pid="" display
|
||||||
local display
|
|
||||||
|
|
||||||
! readQemuPid pid && return 0
|
! readQemuPid pid && return 0
|
||||||
! isAlive "$pid" && return 0
|
! isAlive "$pid" && return 0
|
||||||
@@ -193,9 +240,8 @@ cleanupHelpers() {
|
|||||||
|
|
||||||
startConsole() {
|
startConsole() {
|
||||||
|
|
||||||
local cnt=0
|
|
||||||
local pid=""
|
|
||||||
local output="${1:-/dev/tty}"
|
local output="${1:-/dev/tty}"
|
||||||
|
local cnt=0 pid=""
|
||||||
|
|
||||||
rm -f -- "$CONSOLE_SOCKET" "$CONSOLE_PID"
|
rm -f -- "$CONSOLE_SOCKET" "$CONSOLE_PID"
|
||||||
|
|
||||||
@@ -240,10 +286,34 @@ stopConsole() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startQemu() {
|
||||||
|
|
||||||
|
rm -f -- "$QEMU_START_PID"
|
||||||
|
|
||||||
|
(
|
||||||
|
trap '' INT QUIT
|
||||||
|
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
exec setsid -f -w sh -c '
|
||||||
|
file=$1
|
||||||
|
shift
|
||||||
|
|
||||||
|
"$@" &
|
||||||
|
pid=$!
|
||||||
|
printf "%s\n" "$pid" > "$file" || exit 1
|
||||||
|
|
||||||
|
rc=0
|
||||||
|
wait "$pid" 2>/dev/null || rc=$?
|
||||||
|
exit "$rc"
|
||||||
|
' sh "$QEMU_START_PID" "$@"
|
||||||
|
) </dev/null &
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
finish() {
|
finish() {
|
||||||
|
|
||||||
local reason=$1
|
local reason=$1 failed=0
|
||||||
local failed=0
|
|
||||||
|
|
||||||
if [ ! -f "$QEMU_END" ] && (( reason != 0 )); then
|
if [ ! -f "$QEMU_END" ] && (( reason != 0 )); then
|
||||||
failed=1
|
failed=1
|
||||||
@@ -259,7 +329,7 @@ finish() {
|
|||||||
|
|
||||||
cleanupHelpers
|
cleanupHelpers
|
||||||
|
|
||||||
if ! waitPidFile "$QEMU_PID" 10; then
|
if ! waitQemuExit 10; then
|
||||||
warn "Timed out while waiting for $(app) to exit!"
|
warn "Timed out while waiting for $(app) to exit!"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -278,7 +348,6 @@ normalizeTimeout() {
|
|||||||
|
|
||||||
local term_grace=3 # seconds before loop ends to send SIGTERM
|
local term_grace=3 # seconds before loop ends to send SIGTERM
|
||||||
local cleanup_grace=3 # seconds reserved after the loop for cleanup
|
local cleanup_grace=3 # seconds reserved after the loop for cleanup
|
||||||
local min
|
|
||||||
|
|
||||||
TIMEOUT=$(strip "$TIMEOUT")
|
TIMEOUT=$(strip "$TIMEOUT")
|
||||||
if [[ ! "$TIMEOUT" =~ ^[0-9]+$ ]]; then
|
if [[ ! "$TIMEOUT" =~ ^[0-9]+$ ]]; then
|
||||||
@@ -293,7 +362,7 @@ normalizeTimeout() {
|
|||||||
cleanup_grace=4
|
cleanup_grace=4
|
||||||
fi
|
fi
|
||||||
|
|
||||||
min=$((term_grace + cleanup_grace + 1))
|
local min=$((term_grace + cleanup_grace + 1))
|
||||||
(( TIMEOUT < min )) && (( TIMEOUT = min ))
|
(( TIMEOUT < min )) && (( TIMEOUT = min ))
|
||||||
|
|
||||||
wait_until=$((TIMEOUT - cleanup_grace))
|
wait_until=$((TIMEOUT - cleanup_grace))
|
||||||
@@ -315,11 +384,12 @@ sendAcpiShutdown() {
|
|||||||
abortDuringSetup() {
|
abortDuringSetup() {
|
||||||
|
|
||||||
local code="$1"
|
local code="$1"
|
||||||
|
|
||||||
info "Cannot send ACPI signal during $(app) setup, aborting..."
|
info "Cannot send ACPI signal during $(app) setup, aborting..."
|
||||||
|
|
||||||
sKill "$QEMU_PID"
|
terminateQemu
|
||||||
|
|
||||||
if ! waitPidFile "$QEMU_PID" 5; then
|
if ! waitQemuExit 5; then
|
||||||
warn "Timed out while waiting for $(app) to exit!"
|
warn "Timed out while waiting for $(app) to exit!"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -328,10 +398,9 @@ abortDuringSetup() {
|
|||||||
|
|
||||||
waitForShutdown() {
|
waitForShutdown() {
|
||||||
|
|
||||||
local cnt=0
|
|
||||||
local pid="$1"
|
local pid="$1"
|
||||||
local name="$APP"
|
local name="$APP"
|
||||||
local slp
|
local slp cnt=0
|
||||||
|
|
||||||
while (( cnt <= wait_until && SHUTDOWN_SKIP == 0 )); do
|
while (( cnt <= wait_until && SHUTDOWN_SKIP == 0 )); do
|
||||||
|
|
||||||
@@ -342,7 +411,7 @@ waitForShutdown() {
|
|||||||
! isAlive "$pid" && break
|
! isAlive "$pid" && break
|
||||||
|
|
||||||
# Workaround for stale/zombie QEMU pid file
|
# Workaround for stale/zombie QEMU pid file
|
||||||
[ ! -s "$QEMU_PID" ] && break
|
[ ! -s "$QEMU_START_PID" ] && [ ! -s "$QEMU_PID" ] && break
|
||||||
|
|
||||||
if (( cnt == sigterm_at )); then
|
if (( cnt == sigterm_at )); then
|
||||||
info "${name^} is still running, sending SIGTERM... ($cnt/$wait_until)"
|
info "${name^} is still running, sending SIGTERM... ($cnt/$wait_until)"
|
||||||
@@ -364,8 +433,7 @@ waitForShutdown() {
|
|||||||
graceful_shutdown() {
|
graceful_shutdown() {
|
||||||
|
|
||||||
local sig="$1"
|
local sig="$1"
|
||||||
local pid=""
|
local pid="" code=0
|
||||||
local code=0
|
|
||||||
|
|
||||||
[[ $BASHPID != "$TRAP_PID" ]] && return
|
[[ $BASHPID != "$TRAP_PID" ]] && return
|
||||||
|
|
||||||
@@ -390,9 +458,11 @@ graceful_shutdown() {
|
|||||||
echo && info "Received $sig signal, sending ACPI shutdown signal..."
|
echo && info "Received $sig signal, sending ACPI shutdown signal..."
|
||||||
|
|
||||||
if ! readQemuPid pid; then
|
if ! readQemuPid pid; then
|
||||||
warn "QEMU PID file ($QEMU_PID) does not exist?"
|
if ! interactive || ! waitQemuPid pid; then
|
||||||
|
warn "QEMU PID file does not exist?"
|
||||||
finish "$code"
|
finish "$code"
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$pid" ] || ! isAlive "$pid"; then
|
if [ -z "$pid" ] || ! isAlive "$pid"; then
|
||||||
warn "QEMU process with PID $pid does not exist?"
|
warn "QEMU process with PID $pid does not exist?"
|
||||||
|
|||||||
Reference in New Issue
Block a user