#!/bin/bash # ssd-safe-eject.sh - Safe Eject Extension für USB-C SSDs # Zweck: Sichere Entfernung von USB-C SSDs mit vollständigem Sync und Hardware-Eject set -e # Konfiguration MOUNT_POINT="/mnt/ssd-storage" LOG_FILE="/var/log/ssd-mount.log" # Farben für Ausgabe RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Logging-Funktion log_message() { echo "$(date '+%Y-%m-%d %H:%M:%S') - SAFE-EJECT: $1" | sudo tee -a "$LOG_FILE" echo -e "${BLUE}[SAFE-EJECT]${NC} $1" } log_success() { echo "$(date '+%Y-%m-%d %H:%M:%S') - SUCCESS: $1" | sudo tee -a "$LOG_FILE" echo -e "${GREEN}[✓]${NC} $1" } log_warning() { echo "$(date '+%Y-%m-%d %H:%M:%S') - WARNING: $1" | sudo tee -a "$LOG_FILE" echo -e "${YELLOW}[!]${NC} $1" } log_error() { echo "$(date '+%Y-%m-%d %H:%M:%S') - ERROR: $1" | sudo tee -a "$LOG_FILE" echo -e "${RED}[✗]${NC} $1" } # Schritt 1: Pre-Eject Validierung validate_pre_eject() { log_message "Validiere Pre-Eject Status..." if ! mountpoint -q "$MOUNT_POINT"; then log_warning "Keine SSD auf $MOUNT_POINT gemountet" return 1 fi mounted_device=$(df "$MOUNT_POINT" | tail -1 | awk '{print $1}') log_success "Gemountetes Device identifiziert: $mounted_device" # Prüfe ob Device USB-Device ist device_base=$(echo "$mounted_device" | sed 's/[0-9]*$//') if lsblk -o TRAN "$device_base" 2>/dev/null | grep -q usb; then log_success "USB-Device bestätigt: $device_base" echo "$mounted_device" return 0 else log_error "Device ist kein USB-Device - Safe-Eject nicht anwendbar" return 1 fi } # Schritt 2: Aktive Prozesse prüfen check_active_processes() { local device="$1" log_message "Prüfe aktive Prozesse auf $MOUNT_POINT..." # Prozesse finden die das Mount-Point verwenden active_processes=$(lsof "$MOUNT_POINT" 2>/dev/null || true) if [ -n "$active_processes" ]; then log_warning "Aktive Prozesse gefunden:" echo "$active_processes" echo "" echo "Prozesse beenden? (y/n/s für skip)" read -r response case "$response" in "y"|"Y") log_message "Beende Prozesse..." lsof -t "$MOUNT_POINT" 2>/dev/null | xargs -r sudo kill -TERM sleep 2 # Prüfe erneut remaining=$(lsof "$MOUNT_POINT" 2>/dev/null || true) if [ -n "$remaining" ]; then log_warning "Einige Prozesse laufen noch - Force Kill..." lsof -t "$MOUNT_POINT" 2>/dev/null | xargs -r sudo kill -KILL fi log_success "Prozesse beendet" ;; "s"|"S") log_warning "Prozesse werden nicht beendet - Fortfahren auf eigene Gefahr" ;; *) log_message "Safe-Eject abgebrochen" return 1 ;; esac else log_success "Keine aktiven Prozesse gefunden" fi return 0 } # Schritt 3: Vollständiger Daten-Sync perform_full_sync() { log_message "Führe vollständigen Daten-Sync durch..." # Globaler Sync log_message "Globaler Sync..." sync # Warte auf Dirty Pages Flush log_message "Warte auf Buffer-Flush..." local timeout=30 local count=0 while [ $count -lt $timeout ]; do dirty_pages=$(cat /proc/meminfo | grep "Dirty:" | awk '{print $2}') writeback_pages=$(cat /proc/meminfo | grep "Writeback:" | awk '{print $2}') if [ "$dirty_pages" -le 4 ] && [ "$writeback_pages" -le 4 ]; then log_success "Buffer-Flush abgeschlossen (Dirty: ${dirty_pages}kB, Writeback: ${writeback_pages}kB)" break fi if [ $((count % 5)) -eq 0 ]; then log_message "Warte auf Buffer-Flush... (Dirty: ${dirty_pages}kB, Writeback: ${writeback_pages}kB)" fi sleep 1 count=$((count + 1)) done if [ $count -ge $timeout ]; then log_warning "Buffer-Flush Timeout erreicht - Fortfahren..." fi # Finaler Sync log_message "Finaler Sync..." sync sleep 2 log_success "Vollständiger Sync abgeschlossen" } # Schritt 4: Filesystem Unmount perform_unmount() { local device="$1" log_message "Unmounte Dateisystem: $device" # Lazy Unmount falls normal unmount fehlschlägt if ! sudo umount "$MOUNT_POINT" 2>/dev/null; then log_warning "Normaler Unmount fehlgeschlagen - versuche Lazy Unmount..." if sudo umount -l "$MOUNT_POINT" 2>/dev/null; then log_success "Lazy Unmount erfolgreich" else log_error "Unmount fehlgeschlagen" return 1 fi else log_success "Unmount erfolgreich" fi # Verifikation if mountpoint -q "$MOUNT_POINT"; then log_error "Mount-Point noch aktiv nach Unmount" return 1 else log_success "Mount-Point erfolgreich getrennt" return 0 fi } # Schritt 5: Hardware-Eject perform_hardware_eject() { local device="$1" local device_base=$(echo "$device" | sed 's/[0-9]*$//') log_message "Hardware-Eject für Device: $device_base" # Prüfe ob eject verfügbar ist if ! command -v eject >/dev/null; then log_warning "eject Befehl nicht verfügbar - installiere eject..." sudo apt update && sudo apt install -y eject fi # Hardware-Eject durchführen if sudo eject "$device_base" 2>/dev/null; then log_success "Hardware-Eject erfolgreich: $device_base" else log_warning "Hardware-Eject fehlgeschlagen - möglicherweise nicht unterstützt" # Alternative: USB-Device über sysfs deaktivieren usb_device=$(udevadm info --query=path --name="$device_base" 2>/dev/null | sed 's|/block/.*||') if [ -n "$usb_device" ]; then log_message "Versuche USB-Device Deaktivierung über sysfs..." if echo 1 | sudo tee "/sys$usb_device/remove" >/dev/null 2>&1; then log_success "USB-Device über sysfs deaktiviert" else log_warning "USB-Device Deaktivierung fehlgeschlagen" fi fi fi } # Schritt 6: Post-Eject Validierung validate_post_eject() { local device="$1" local device_base=$(echo "$device" | sed 's/[0-9]*$//') log_message "Post-Eject Validierung..." # Prüfe ob Mount-Point noch aktiv if mountpoint -q "$MOUNT_POINT"; then log_error "Mount-Point noch aktiv - Safe-Eject unvollständig" return 1 fi # Prüfe ob Device noch sichtbar sleep 3 # Kurze Wartezeit für System-Update if [ -b "$device_base" ]; then log_warning "Device noch im System sichtbar: $device_base" log_message "Device kann möglicherweise manuell entfernt werden" else log_success "Device nicht mehr im System sichtbar" fi log_success "Post-Eject Validierung abgeschlossen" return 0 } # Safe-Eject Status anzeigen show_eject_status() { echo "" echo "=== Safe-Eject Status ===" if mountpoint -q "$MOUNT_POINT"; then mounted_device=$(df "$MOUNT_POINT" | tail -1 | awk '{print $1}') device_base=$(echo "$mounted_device" | sed 's/[0-9]*$//') echo "✓ Gemountetes Device: $mounted_device" echo "✓ Hardware Device: $device_base" # USB-Status prüfen if lsblk -o TRAN "$device_base" 2>/dev/null | grep -q usb; then echo "✓ USB-Device: Ja" echo "✓ Safe-Eject: Verfügbar" else echo "✗ USB-Device: Nein" echo "✗ Safe-Eject: Nicht verfügbar" fi # Aktive Prozesse prüfen active_count=$(lsof "$MOUNT_POINT" 2>/dev/null | wc -l) if [ "$active_count" -gt 0 ]; then echo "! Aktive Prozesse: $active_count" else echo "✓ Aktive Prozesse: Keine" fi else echo "✗ Kein Device gemountet" echo "✗ Safe-Eject: Nicht erforderlich" fi echo "" } # Hauptfunktion Safe-Eject safe_eject_main() { echo "=== USB-C SSD Safe-Eject ===" echo "Version: 1.0" echo "" # Pre-Validierung device=$(validate_pre_eject) if [ $? -ne 0 ]; then return 1 fi echo "=== Safe-Eject Prozess gestartet für: $device ===" echo "" # Aktive Prozesse prüfen if ! check_active_processes "$device"; then return 1 fi # Vollständiger Sync perform_full_sync # Unmount if ! perform_unmount "$device"; then return 1 fi # Hardware-Eject perform_hardware_eject "$device" # Post-Validierung validate_post_eject "$device" echo "" echo "=== Safe-Eject abgeschlossen ===" echo "USB-C SSD kann jetzt sicher entfernt werden." echo "" return 0 } # Hauptfunktion main() { case "${1:-eject}" in "eject") safe_eject_main ;; "status") show_eject_status ;; *) echo "Verwendung: $0 {eject|status}" echo "" echo "Befehle:" echo " eject - Sichere SSD-Entfernung durchführen" echo " status - Safe-Eject Status anzeigen" ;; esac } # Script ausführen main "$@"