From 7b095702fca46ff36a28c6b1add76e3328c0b839 Mon Sep 17 00:00:00 2001 From: Kroese Date: Sun, 5 Jul 2026 22:09:49 +0200 Subject: [PATCH] feat: Refactor shutdown logic (#1204) --- src/power.sh | 196 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 132 insertions(+), 64 deletions(-) diff --git a/src/power.sh b/src/power.sh index 4b4a815..68ad9f3 100644 --- a/src/power.sh +++ b/src/power.sh @@ -28,38 +28,85 @@ app() { echo "$APP" && return 0 } -finish() { +signalCode() { + local sig="$1" - local i=0 + case "$sig" in + SIGHUP) echo 129 ;; + SIGINT) echo 130 ;; + SIGQUIT) echo 131 ;; + SIGABRT) echo 134 ;; + SIGTERM) echo 143 ;; + *) echo 0 ;; + esac + + return 0 +} + +displayReason() { + local reason="$1" + + case "$reason" in + 129 ) echo "SIGHUP" ;; + 130 ) echo "SIGINT" ;; + 131 ) echo "SIGQUIT" ;; + 134 ) echo "SIGABRT" ;; + 143 ) echo "SIGTERM" ;; + * ) echo "$reason" ;; + esac + + return 0 +} + +readQemuPid() { + local file="$1" + local __var="$2" local pid="" - local reason=$1 - local pids=( "${HOST_PID:-}" "${WSD_PID:-}" \ - "${WEB_PID:-}" "${PASST_PID:-}" "${DNSMASQ_PID:-}" ) - touch "$QEMU_END" + [ -s "$file" ] || return 1 + read -r pid <"$file" || return 1 + [ -n "$pid" ] || return 1 - 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 + printf -v "$__var" '%s' "$pid" + return 0 +} + +forceKillQemu() { + local reason="$1" + local pid="" + local display + + if readQemuPid "$QEMU_PID" pid; then + if isAlive "$pid"; then + display=$(displayReason "$reason") + error "Forcefully terminating $(app), reason: $display..." + { disown "$pid" || :; kill -9 -- "$pid" || :; } 2>/dev/null fi fi + return 0 +} + +cleanupHelpers() { + local pids=( "${HOST_PID:-}" "${WSD_PID:-}" \ + "${WEB_PID:-}" "${PASST_PID:-}" "${DNSMASQ_PID:-}" ) + mKill "${pids[@]}" fKill "print.sh" - closeNetwork + return 0 +} + +finish() { + + local reason=$1 + + touch "$QEMU_END" + + forceKillQemu "$reason" + cleanupHelpers + if ! waitPidFile "$QEMU_PID" 10; then warn "Timed out while waiting for $(app) to exit!" fi @@ -68,42 +115,10 @@ finish() { exit "$reason" } -graceful_shutdown() { - - local sig="$1" - local pid="" - local code=0 - local start url response elapsed - - [[ $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 - start=$SECONDS - touch "$QEMU_END" - echo && info "Received $1 signal, sending shutdown command..." - - 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 +sendGuestShutdown() { + local pid="$1" + local response + local url # Don't send the powerdown signal because vDSM ignores ACPI signals # nc -q 1 -w 1 -U "$QEMU_DIR/monitor.sock" &> /dev/null <<<'system_powerdown' || : @@ -126,11 +141,14 @@ graceful_shutdown() { fi - local name - name="$(app)" + return 0 +} - local term_grace=3 # seconds before loop ends to send SIGTERM - local cleanup_grace=3 # seconds reserved after the loop for cleanup +normalizeTimeout() { + local min + + term_grace=3 # seconds before loop ends to send SIGTERM + cleanup_grace=3 # seconds reserved after the loop for cleanup TIMEOUT=$(strip "$TIMEOUT") if [[ ! "$TIMEOUT" =~ ^[0-9]+$ ]]; then @@ -145,8 +163,6 @@ graceful_shutdown() { cleanup_grace=4 fi - local cnt=0 sigterm_at=0 min wait_until elapsed timeout_left - elapsed=$((SECONDS - start)) timeout_left=$((TIMEOUT - elapsed)) @@ -156,10 +172,19 @@ graceful_shutdown() { wait_until=$((timeout_left - cleanup_grace)) sigterm_at=$((wait_until - term_grace)) + return 0 +} + +waitForShutdown() { + local cnt=0 + local name="$1" + local pid="$2" + local slp + while (( cnt <= wait_until )); do sleep 1 & - local slp=$! + slp=$! # Stop waiting if the process has exited ! isAlive "$pid" && break @@ -179,6 +204,49 @@ graceful_shutdown() { done + return 0 +} + +graceful_shutdown() { + + local sig="$1" + local pid="" + local code=0 + local name + local term_grace cleanup_grace + local sigterm_at=0 wait_until=0 elapsed timeout_left + local start + + [[ $BASHPID != "$TRAP_PID" ]] && return + + code=$(signalCode "$sig") + + if [ -f "$QEMU_END" ]; then + echo && info "Received $1 signal while already shutting down..." + return + fi + + set +e + start=$SECONDS + touch "$QEMU_END" + echo && info "Received $1 signal, sending shutdown command..." + + if ! readQemuPid "$QEMU_PID" pid; then + warn "QEMU PID file ($QEMU_PID) does not exist?" + finish "$code" + fi + + if ! isAlive "$pid"; then + warn "QEMU process with PID $pid does not exist?" + finish "$code" + fi + + sendGuestShutdown "$pid" + + name="$(app)" + normalizeTimeout + waitForShutdown "$name" "$pid" + finish "$code" }