feat(qemu-ga): failure-propagating fsfreeze dispatcher; enabled on nh3-svos (hooks were never run)
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
# fsfreeze-dispatch: qemu-ga fsfreeze hook dispatcher that PROPAGATES failure (eshpfi configs/qemu-ga/, 2026-10-05).
|
||||
#
|
||||
# Debian's stock /etc/qemu/fsfreeze-hook logs a child hook's nonzero status but always exits 0, so a hook that
|
||||
# refuses to quiesce still lets the snapshot proceed (found by svos-dev on nh3-svos). This one does not:
|
||||
# freeze: run hooks in name order; on the first failure, thaw the FAILING hook and then every hook that had
|
||||
# completed, in reverse order (best effort), log any unwind failure, and exit 1.
|
||||
# thaw: run every hook in reverse name order, exit 1 if any failed (never stop early).
|
||||
# Wired in by /etc/qemu/qemu-ga.conf `fsfreeze-hook=/usr/local/sbin/fsfreeze-dispatch`. The packaged
|
||||
# /etc/qemu/fsfreeze-hook (a dpkg conffile) is left untouched. Same hook dir and log file as the stock hook.
|
||||
set -u
|
||||
HOOKDIR=/etc/qemu/fsfreeze-hook.d
|
||||
LOGFILE=/var/log/qga-fsfreeze-hook.log
|
||||
action=${1:-}
|
||||
|
||||
log() { printf '%s: %s\n' "$(date)" "$1" >>"$LOGFILE" 2>/dev/null || logger -t qemu-ga-freeze-hook "$1"; }
|
||||
|
||||
ignored() {
|
||||
case "$1" in
|
||||
*~ | *.bak | *.orig | *.rpmnew | *.rpmorig | *.rpmsave | *.sample | *.dpkg-old | *.dpkg-new | *.dpkg-tmp | \
|
||||
*.dpkg-dist | *.dpkg-bak | *.dpkg-backup | *.dpkg-remove) return 0 ;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
hooks=()
|
||||
if [ -d "$HOOKDIR" ]; then
|
||||
while IFS= read -r -d '' f; do
|
||||
ignored "$f" && continue
|
||||
[ -f "$f" ] && [ -x "$f" ] && hooks+=("$f")
|
||||
done < <(find "$HOOKDIR" -mindepth 1 -maxdepth 1 -print0 | LC_ALL=C sort -z)
|
||||
fi
|
||||
|
||||
run() { # $1 hook, $2 action
|
||||
log "Executing $1 $2"
|
||||
"$1" "$2" >>"$LOGFILE" 2>&1
|
||||
local st=$?
|
||||
[ "$st" -ne 0 ] && log "Error: $1 $2 finished with status=$st"
|
||||
return "$st"
|
||||
}
|
||||
|
||||
case "$action" in
|
||||
freeze)
|
||||
completed=()
|
||||
for h in "${hooks[@]}"; do
|
||||
if run "$h" freeze; then
|
||||
completed+=("$h")
|
||||
else
|
||||
log "freeze FAILED at $h: unwinding (thaw $h, then ${#completed[@]} completed hook(s) in reverse)"
|
||||
unwind_failed=0
|
||||
run "$h" thaw || unwind_failed=1
|
||||
for ((i = ${#completed[@]} - 1; i >= 0; i--)); do run "${completed[$i]}" thaw || unwind_failed=1; done
|
||||
[ "$unwind_failed" -ne 0 ] && log "UNWIND FAILURE: at least one thaw during the freeze unwind failed"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
exit 0 ;;
|
||||
thaw)
|
||||
rc=0
|
||||
for ((i = ${#hooks[@]} - 1; i >= 0; i--)); do run "${hooks[$i]}" thaw || rc=1; done
|
||||
[ "$rc" -ne 0 ] && log "thaw: at least one hook failed"
|
||||
exit "$rc" ;;
|
||||
*)
|
||||
log "fsfreeze-dispatch: unknown action '$action'"
|
||||
exit 1 ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user