#!/bin/bash
# vm_timegap_watch.sh — erkennt Stillstand der VM (Live-Migration, Hypervisor-Stall).
#
# Misst im Sekundentakt, wieviel Wanduhrzeit tatsaechlich vergangen ist. Steht die VM,
# laeuft der Prozess nicht weiter, und beim Auftauen ist die Luecke sichtbar. Das erfasst
# den Freeze unabhaengig davon, wie sich CLOCK_MONOTONIC und CLOCK_REALTIME dabei verhalten
# — chrony und dmesg protokollieren kurze Freezes nicht zuverlaessig.
#
# Wird minuetlich per cron mit "timeout 59" gestartet; daher eine Blindstelle von ~1 s
# pro Minute. Bewusst so, um die kompilierte s6-rc-Servicedatenbank nicht anfassen zu muessen.
set -u

STATE=/var/lib/vm-timegap
THRESH_MS=${THRESH_MS:-3000}     # ab dieser Luecke gilt es als Freeze
MAX_EVENTS=500

mkdir -p "$STATE"
[ -f "$STATE/count" ] || echo 0 > "$STATE/count"

# PID-Datei statt Kommandozeilensuche: pgrep -f wuerde auch fremde Prozesse treffen,
# die den Skriptnamen nur erwaehnen (etwa eine Shell, die ihn aufruft).
# pid: supervised by s6 (svctl isup vm-timegap)

# Millisekunden aus Nanosekunden. NICHT "date +%s%3N" verwenden: die hier eingesetzten
# uutils-coreutils (0.8.0) ignorieren den Breiten-Modifikator und liefern volle Nanosekunden,
# wodurch jede normale Sekunde als Freeze gezaehlt wuerde.
now_ms() {
    local n
    n=$(date +%s%N) || return 1
    case "$n" in ''|*[!0-9]*) return 1 ;; esac
    echo $(( n / 1000000 ))
}

prev=$(now_ms) || { echo "vm_timegap_watch: date +%s%N liefert keine Nanosekunden" >&2; exit 1; }
while :; do
    sleep 1
    now=$(now_ms) || continue
    delta=$(( now - prev ))
    prev=$now

    # 1000 ms sind normal; alles deutlich darueber ist verlorene Zeit
    [ "$delta" -le "$THRESH_MS" ] && continue

    ts=$(( now / 1000 ))
    lost=$(( delta - 1000 ))
    echo "$ts $lost" >> "$STATE/events"
    echo "$(( $(cat "$STATE/count" 2>/dev/null || echo 0) + 1 ))" > "$STATE/count"
    echo "$ts"   > "$STATE/last_ts"
    echo "$lost" > "$STATE/last_ms"

    # Ereignisdatei begrenzen
    if [ "$(wc -l < "$STATE/events")" -gt "$MAX_EVENTS" ]; then
        tail -n $(( MAX_EVENTS / 2 )) "$STATE/events" > "$STATE/events.tmp" \
            && mv "$STATE/events.tmp" "$STATE/events"
    fi
done
