#!/bin/bash
# WIPE-STATION – Laufwerke automatisch durchsuchen, löschen, prüfen, SMART bewerten
# Für SystemRescue 13.x. Ablage auf dem Stick: /autorun/autorun
# Quelle und Anleitung: https://hddlab.de/festplattenvernichtung/wipe-station-selbst-bauen/
#
# ACHTUNG: Dieses Skript löscht ALLE Laufwerke des Rechners außer dem Boot-Stick.
# Nur auf einem Rechner starten, der ausschließlich zum Löschen dient.
# Ohne Gewähr. Zuerst mit einer leeren Testplatte ausprobieren.

BOOT=${BOOT:-/run/archiso/bootmnt}   # hier hängt der Stick in SystemRescue
START_DELAY=10       # Sekunden Vorwarnung vor dem Löschen (0 = sofort)
SLOW_MBS=40          # darunter gilt ein Laufwerk als LANGSAM
STALL_SEC=60         # so lange ohne Fortschritt = HÄNGT
GIVEUP_SEC=900       # so lange ohne Fortschritt = Laufwerk wird abgebrochen
REALLOC_WARN=1       # ab so vielen umgelagerten Sektoren: gelb
REALLOC_BAD=50       # ab so vielen: rot
TEMP_WARN=55         # °C
SEARCH_TIMEOUT=3600  # Sekunden Dateisuche je Partition
SEARCH_DELETED=1     # gelöschte Dateien auf NTFS mitsuchen (1/0)

# --- 1. Aufruf durch autorun: als eigener Dienst auf Konsole 8 neu starten -----------------
if [ "${1:-}" != run ]; then
  systemd-run --unit=wipestation --property=TTYPath=/dev/tty8 \
    --property=StandardOutput=tty --property=StandardError=tty \
    /bin/bash "$BOOT/autorun/autorun" run
  exit 0
fi

R=$'\e[1;31m'; G=$'\e[1;32m'; Y=$'\e[1;33m'; N=$'\e[0m'
chvt 8 2>/dev/null
setterm --blank 0 --powerdown 0 2>/dev/null

# --- 2. Berichtsordner auf dem Stick (Stick beschreibbar machen) ---------------------------
mount -o remount,rw "$BOOT" 2>/dev/null
OUT="$BOOT/wipe-reports"
if ! { mkdir -p "$OUT" && touch "$OUT/.test"; } 2>/dev/null; then
  OUT=/root/wipe-reports; mkdir -p "$OUT"
  echo "${Y}Stick nicht beschreibbar – Berichte nur in $OUT (gehen beim Ausschalten verloren)${N}"
fi
rm -f "$OUT/.test"
[ -f "$OUT/log.csv" ] || echo "Datum;Modell;Seriennummer;Größe;Ergebnis;Bericht" >"$OUT/log.csv"
WORK=/tmp/wipe; mkdir -p "$WORK"
BOOTDISK=$(lsblk -no PKNAME "$(findmnt -no SOURCE "$BOOT" 2>/dev/null)" 2>/dev/null | head -1)

# Suchmasken aus search.txt (eine Maske je Zeile, # = Kommentar)
NAMEN=(); FINDARGS=()
if [ -f "$BOOT/search.txt" ]; then
  while IFS= read -r m; do
    m=${m%%#*}; m=$(printf '%s' "$m" | tr -d '\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
    [ -z "$m" ] && continue
    [ ${#FINDARGS[@]} -gt 0 ] && FINDARGS+=(-o)
    case $m in */*) FINDARGS+=(-ipath "$m") ;; *) FINDARGS+=(-iname "$m"); NAMEN+=("$m") ;; esac
  done <"$BOOT/search.txt"
fi

# --- 3. Laufwerke finden: alles Beschreibbare außer Stick, loop, zram, CD ------------------
laufwerke() {
  if [ -n "${TEST_DISKS:-}" ]; then printf '%s\n' $TEST_DISKS; return; fi
  lsblk -dnpo NAME,TYPE,RO | while read -r d t ro; do
    [ "$t" = disk ] && [ "$ro" = 0 ] || continue
    case $d in /dev/loop*|/dev/zram*|/dev/sr*) continue ;; esac
    [ -n "$BOOTDISK" ] && [ "$d" = "/dev/$BOOTDISK" ] && continue
    lsblk -no LABEL "$d" 2>/dev/null | grep -q '^RESCUE' && continue
    echo "$d"
  done
}
sn_von() { lsblk -dno SERIAL "$1" 2>/dev/null | tr -d ' '; }

# --- 4. Schutz: Seriennummer, Markierung, Datei des Kunden gefunden -> nicht löschen -------
geschuetzt() {  # $1 Laufwerk, $2 Fundliste. Rückgabe 0 = Laufwerk NICHT löschen
  local d=$1 fund=$2 sn p fs lab mp m
  : >"$fund"
  sn=$(sn_von "$d")
  if [ -n "$sn" ] && [ -f "$BOOT/nowipe.txt" ] && tr -d '\r ' <"$BOOT/nowipe.txt" | grep -qixF "$sn"; then
    echo "Seriennummer $sn steht in nowipe.txt" >>"$fund"
  fi
  mp=$(mktemp -d /mnt/ws.XXXX)
  for p in $(lsblk -lnpo NAME "$d"); do
    fs=$(blkid -o value -s TYPE "$p" 2>/dev/null); lab=$(blkid -o value -s LABEL "$p" 2>/dev/null)
    case $fs in ''|swap|LVM2_member|linux_raid_member|crypto_LUKS|BitLocker|zfs_member) continue ;; esac
    if printf '%s' "$lab" | grep -qiE 'NOWIPE|NO_WIPE|KEEP|NICHT_LOESCHEN'; then echo "Volume-Label '$lab' auf $p" >>"$fund"; continue; fi
    if [ "$fs" = ntfs ]; then
      mount -t ntfs3 -o ro "$p" "$mp" 2>/dev/null || ntfs-3g -o ro,norecover "$p" "$mp" 2>/dev/null
    else
      mount -o ro "$p" "$mp" 2>/dev/null
    fi || continue
    find "$mp" -maxdepth 1 \( -iname 'nowipe*' -o -iname 'no_wipe*' -o -iname 'do_not_wipe*' \
      -o -iname 'nicht_loeschen*' -o -iname 'nicht_löschen*' -o -iname 'не_стирать*' \) 2>/dev/null \
      | sed "s|^$mp|Markierung auf $p: |" >>"$fund"
    if [ ${#FINDARGS[@]} -gt 0 ]; then
      timeout "$SEARCH_TIMEOUT" find "$mp" \( "${FINDARGS[@]}" \) -print 2>/dev/null | head -5000 \
        | sed "s|^$mp|$p: |" >>"$fund"
    fi
    umount "$mp"
    if [ "$fs" = ntfs ] && [ "$SEARCH_DELETED" = 1 ]; then
      for m in "${NAMEN[@]}"; do
        ntfsundelete "$p" --scan --match "$m" 2>/dev/null | awk -v p="$p" '$1 ~ /^[0-9]+$/ {print p ": gelöscht, Inode " $1 ": " $NF}' >>"$fund"
      done
    fi
  done
  rmdir "$mp"
  [ -s "$fund" ]
}

# --- 5. SMART lesen und bewerten -----------------------------------------------------------
smart() {  # $1 Laufwerk, $2 Datei
  smartctl -x "$1" >"$2" 2>&1
  grep -qiE 'Unknown USB bridge|specify device type' "$2" && smartctl -x -d sat "$1" >"$2" 2>&1
  return 0
}
attr() { awk -v id="$1" '$1==id && NF>=8 {print $8+0; exit}' "$2"; }   # Rohwert aus smartctl -x
zahl() { grep -oE "$1 *[0-9]+" "$2" | grep -oE '[0-9]+$' | head -1; }

bewerten() {  # $1 SMART vorher, $2 SMART nachher, $3 Ergebnisliste -> gibt OK/WARN/BAD aus
  local v=$1 n=$2 r=$3 s=OK a b id
  rot()  { echo "[XX] $*" >>"$r"; s=BAD; }
  gelb() { echo "[!!] $*" >>"$r"; [ "$s" = OK ] && s=WARN; return 0; }
  if ! grep -qE 'overall-health.*PASSED|SMART Health Status: OK' "$n"; then
    if grep -qiE 'Unavailable|Unknown USB bridge|specify device type|Unable to detect|not found' "$n"; then gelb "SMART nicht lesbar"; else rot "SMART-Gesamtstatus nicht PASSED/OK"; fi
  fi
  grep -q FAILING_NOW "$n" && rot "Attribut mit FAILING_NOW"
  grep -q In_the_past "$n" && gelb "Schwellwert in der Vergangenheit überschritten"
  for id in 5 196; do
    a=$(attr $id "$n"); [ -z "$a" ] && continue
    if [ "$a" -ge "$REALLOC_BAD" ]; then rot "Attribut $id (umgelagert) = $a"
    elif [ "$a" -ge "$REALLOC_WARN" ]; then gelb "Attribut $id (umgelagert) = $a"; fi
  done
  for id in 187 197 198; do
    a=$(attr $id "$n"); [ "${a:-0}" -ge 1 ] && rot "Attribut $id (schwebend/unkorrigierbar) = $a"
  done
  for id in 5 187 197 198; do
    a=$(attr $id "$v"); b=$(attr $id "$n")
    [ -n "$a" ] && [ -n "$b" ] && [ "$b" -gt "$a" ] && rot "Attribut $id während des Tests gestiegen: $a → $b"
  done
  a=$(attr 10 "$n");  [ "${a:-0}" -ge 1 ] && gelb "Spin Retry = $a"
  a=$(attr 199 "$n"); [ "${a:-0}" -ge 1 ] && gelb "CRC-Fehler = $a (Kabel/Adapter prüfen)"
  grep -qE '(ATA|Device) Error Count: *[1-9]' "$n" && gelb "Einträge im SMART-Fehlerprotokoll"
  a=$(attr 194 "$n"); [ -z "$a" ] && a=$(attr 190 "$n")
  [ "${a:-0}" -gt "$TEMP_WARN" ] && gelb "Temperatur $a °C"
  if grep -q 'Critical Warning:' "$n"; then                      # NVMe
    grep -qE 'Critical Warning: *0x00' "$n" || rot "NVMe Critical Warning gesetzt"
    a=$(zahl 'Media and Data Integrity Errors:' "$n"); [ "${a:-0}" -ge 1 ] && rot "NVMe Media Errors = $a"
    a=$(zahl 'Percentage Used:' "$n")
    if [ "${a:-0}" -ge 100 ]; then rot "NVMe verbraucht $a %"; elif [ "${a:-0}" -ge 80 ]; then gelb "NVMe verbraucht $a %"; fi
    a=$(zahl 'Available Spare:' "$n")
    if [ -n "$a" ] && [ "$a" -le 10 ]; then rot "NVMe Reserve $a %"; elif [ -n "$a" ] && [ "$a" -lt 50 ]; then gelb "NVMe Reserve $a %"; fi
  fi
  a=$(zahl 'Elements in grown defect list:' "$n")                  # SAS
  if [ "${a:-0}" -ge "$REALLOC_BAD" ]; then rot "SAS Grown Defects = $a"; elif [ "${a:-0}" -ge 1 ]; then gelb "SAS Grown Defects = $a"; fi
  echo "$s"
}

# --- 6. Ein Laufwerk komplett bearbeiten (läuft parallel für jedes Laufwerk) ---------------
bearbeiten() {
  local d=$1 W sn modell groesse rep stamp e s
  W=$WORK/$(basename "$1"); mkdir -p "$W"; sn=$(sn_von "$d"); modell=$(lsblk -dno MODEL "$d" | sed 's/ *$//;s/ /_/g')
  groesse=$(blockdev --getsize64 "$d"); echo "$groesse" >"$W/size"
  stamp=$(date +%Y%m%d-%H%M); rep="$OUT/${stamp}_${modell:-Laufwerk}_${sn:-$(basename "$d")}.txt"
  : >"$W/ergebnis"
  echo SMART >"$W/phase"; smart "$d" "$W/smart_vorher"
  rm -f "$W/last" "$W/still"; echo LÖSCHEN >"$W/phase"; awk '{print $7}' "/sys/block/$(basename "$d")/stat" >"$W/start"
  dd if=/dev/zero of="$d" bs=4M oflag=direct conv=fsync status=none 2>"$W/dd.err" & echo $! >"$W/pid"; wait $!
  e=$?
  if [ -f "$W/hung" ]; then echo "[XX] Laufwerk hängt beim Schreiben – abgebrochen" >>"$W/ergebnis"
  elif [ $e -ne 0 ] && ! grep -q 'No space left' "$W/dd.err"; then echo "[XX] Schreibfehler: $(head -1 "$W/dd.err")" >>"$W/ergebnis"; fi
  if ! grep -q '^\[XX\]' "$W/ergebnis"; then
    rm -f "$W/last" "$W/still"; echo PRÜFEN >"$W/phase"; blockdev --flushbufs "$d"; echo 3 >/proc/sys/vm/drop_caches
    awk '{print $3}' "/sys/block/$(basename "$d")/stat" >"$W/start"
    cmp -n "$groesse" "$d" /dev/zero >"$W/cmp.out" 2>&1 & echo $! >"$W/pid"; wait $!
    e=$?
    if [ -f "$W/hung" ]; then echo "[XX] Laufwerk hängt beim Lesen – abgebrochen" >>"$W/ergebnis"
    elif [ $e -eq 1 ]; then echo "[XX] Prüfung: nicht überall Nullen ($(head -1 "$W/cmp.out"))" >>"$W/ergebnis"
    elif [ $e -ne 0 ]; then echo "[XX] Lesefehler bei der Prüfung" >>"$W/ergebnis"; fi
  fi
  [ -s "$W/langsam" ] && echo "[!!] Langsam: $(cat "$W/langsam")" >>"$W/ergebnis"
  echo SMART >"$W/phase"; smart "$d" "$W/smart_nachher"
  s=$(bewerten "$W/smart_vorher" "$W/smart_nachher" "$W/ergebnis")
  grep -q '^\[XX\]' "$W/ergebnis" && s=BAD
  [ -f "$W/hung" ] && s=HUNG
  { echo "WIPE-STATION Bericht – $(date '+%d.%m.%Y %H:%M')"; echo "Laufwerk: $modell  SN: $sn  Größe: $groesse Bytes"
    echo "Ergebnis: $s"; echo "Gelöscht: Nullen über die gesamte Kapazität, danach vollständig gelesen und geprüft"
    cat "$W/ergebnis"; echo; echo "===== SMART nachher ====="; cat "$W/smart_nachher"
    echo; echo "===== SMART vorher ====="; cat "$W/smart_vorher"; } >"$rep"
  echo "$(date '+%Y-%m-%d %H:%M');$modell;$sn;$groesse;$s;$(basename "$rep")" >>"$OUT/log.csv"
  echo "$s" >"$W/status"; echo FERTIG >"$W/phase"; sync
}

# --- 7. Bildschirm: Fortschritt, Tempo, Hänger ---------------------------------------------
anzeigen() {
  local W n ph size start now done_ pct alt mbs still ziel
  clear; echo "WIPE-STATION   $(date '+%H:%M:%S')   Berichte: $OUT"; echo
  for W in "$WORK"/*/; do
    W=${W%/}; n=$(basename "$W"); [ -f "$W/phase" ] || continue
    ph=$(cat "$W/phase"); printf '%-8s %-30s ' "$n" "$(cat "$W/name" 2>/dev/null)"
    case $ph in
      FERTIG) case $(cat "$W/status") in OK) echo "${G}ALLES IN ORDNUNG${N}";; WARN) echo "${Y}ACHTUNG${N}";; *) echo "${R}SCHLECHT ($(cat "$W/status"))${N}";; esac
              sed 's/^/           /' "$W/ergebnis"; continue ;;
      GESCHÜTZT) echo "${G}GESCHÜTZT – WIRD NICHT GELÖSCHT${N}"; head -5 "$W/fund" | sed 's/^/           /'; continue ;;
      LÖSCHEN) still=7 ;; PRÜFEN) still=3 ;; *) echo "$ph"; continue ;;
    esac
    size=$(cat "$W/size"); start=$(cat "$W/start")
    now=$(awk -v f=$still '{print $f}' "/sys/block/$n/stat")
    done_=$(( (now - start) * 512 )); pct=$(( done_ * 100 / size ))
    alt=$(cat "$W/last" 2>/dev/null || echo "$now"); echo "$now" >"$W/last"
    mbs=$(( (now - alt) * 512 / 5 / 1048576 ))
    if [ "$now" = "$alt" ]; then echo $(( $(cat "$W/still" 2>/dev/null || echo 0) + 5 )) >"$W/still"; else echo 0 >"$W/still"; fi
    still=$(cat "$W/still")
    if [ "$still" -ge "$GIVEUP_SEC" ] && [ ! -f "$W/hung" ]; then touch "$W/hung"; kill -9 "$(cat "$W/pid")" 2>/dev/null; fi
    if [ "$still" -ge "$STALL_SEC" ]; then ziel="${R}HÄNGT seit ${still}s${N}"
    elif [ "$mbs" -lt "$SLOW_MBS" ] && [ "$still" -eq 0 ]; then ziel="${Y}LANGSAM${N}"; echo "$ph ${mbs} MB/s um $(date +%H:%M)" >>"$W/langsam.roh"
      echo "$(wc -l <"$W/langsam.roh") Messungen unter $SLOW_MBS MB/s, minimal $(grep -oE '[0-9]+ MB/s' "$W/langsam.roh" | sort -n | head -1)" >"$W/langsam"
    else ziel="${G}normal${N}"; fi
    printf '%-9s %3d %%  %5d MB/s  %s\n' "$ph" "$pct" "$mbs" "$ziel"
  done
}

# --- 8. Hauptschleife: Laufwerke starten, Bildschirm aktualisieren, neue Laufwerke erkennen --
erledigt=" "
erstes=1
while true; do
  neu=()
  for d in $(laufwerke); do
    sn=$(sn_von "$d"); key=${sn:-$d}
    case $erledigt in *" $key "*) continue ;; esac
    neu+=("$d"); erledigt="$erledigt$key "
  done
  if [ ${#neu[@]} -gt 0 ]; then
    for d in "${neu[@]}"; do
      W=$WORK/$(basename "$d"); rm -rf "$W"; mkdir -p "$W"
      echo "$(lsblk -dno MODEL,SIZE "$d" | tr -s ' ')" >"$W/name"; echo SUCHE >"$W/phase"
    done
    anzeigen; echo; echo "Durchsuche Laufwerke nach Kundendateien und Markierungen …"
    starten=()
    for d in "${neu[@]}"; do
      W=$WORK/$(basename "$d")
      if geschuetzt "$d" "$W/fund"; then
        echo GESCHÜTZT >"$W/phase"; sn=$(sn_von "$d"); cp "$W/fund" "$OUT/$(date +%Y%m%d-%H%M)_FOUND_${sn:-$(basename "$d")}.txt"
        echo "$(date '+%Y-%m-%d %H:%M');$(lsblk -dno MODEL "$d");$(sn_von "$d");$(blockdev --getsize64 "$d");PROTECTED;" >>"$OUT/log.csv"
      else starten+=("$d"); echo WARTET >"$W/phase"; fi
    done
    if [ ${#starten[@]} -gt 0 ]; then
      for ((t = START_DELAY; t > 0; t--)); do
        anzeigen; echo; echo "${R}Folgende Laufwerke werden in $t s VOLLSTÄNDIG GELÖSCHT: ${starten[*]}${N}"; sleep 1
      done
      for d in "${starten[@]}"; do bearbeiten "$d" & done
    fi
  fi
  [ $erstes = 1 ] && [ -z "$(laufwerke)" ] && echo "Keine Laufwerke gefunden."
  erstes=0
  anzeigen; sleep 5
done
