67 lines
2.6 KiB
Bash
67 lines
2.6 KiB
Bash
#!/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
|