#!/bin/bash # ============================================================================================== # ================================= Mover Stop ================================================= # ============================================================================================== # Safely stops the unRAID mover process with a warning before halting. # Warns all logged-in users via wall message, waits the configured timeout, then stops. # # ── WHEN TO USE ─────────────────────────────────────────────────────────────────────────────── # - Before a planned reboot when mover is running mid-cycle # - Before disk replacement or array operations that need mover stopped # - Before rsync — mover and rsync simultaneously moving the same files causes corruption # - Called automatically by maintenance scripts that need the mover stopped first # # ── STOP SEQUENCE ───────────────────────────────────────────────────────────────────────────── # 1. Check if mover is running — exit cleanly if not # 2. Broadcast wall warning to all logged-in users # 3. Wait MOVER_STOP_TIMEOUT seconds (default 30) — gives active sessions a chance to note it # 4. Send SIGTERM — mover can complete its current file operation before exiting # 5. Wait 5 seconds for graceful exit # 6. Verify stopped — if still running send SIGKILL (force) # 7. Final verify — error if still running after SIGKILL # # ── SIGTERM vs SIGKILL ──────────────────────────────────────────────────────────────────────── # SIGTERM first — allows mover to finish the file it is currently moving (no partial files). # SIGKILL only as fallback — forces immediate stop (may leave partial files on cache or array). # # ── SAFEGUARDS ──────────────────────────────────────────────────────────────────────────────── # acquire_lock — prevents concurrent stop attempts racing each other # Root check — pkill on emhttp processes requires root # validate_unraid — notify validated before use # SIGTERM → verify → SIGKILL sequence — graceful then forced # Final verify — confirms mover actually stopped # Silent on clean — mover not running = log() only, no output ✅ # # ── CONFIGURATION (master.conf) ─────────────────────────────────────────────────────────────── # MOVER_STOP_TIMEOUT — seconds to warn users before stopping (default 30) # # ── USAGE ───────────────────────────────────────────────────────────────────────────────────── # mover_stop.sh — stop mover with configured timeout # mover_stop.sh --dry-run — show what would happen # mover_stop.sh --status — show mover state # mover_stop.sh --log — verbose output # ============================================================================================== SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../load_config.sh" parse_args "$@" # ============================================================================================== # ━━━ Setup ━━━ # ============================================================================================== if [[ "$EUID" -ne 0 ]]; then error "Must be run as root — pkill on emhttp processes requires root" exit 1 fi validate_unraid_cmd \ "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ "" "" \ "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" validate_int MOVER_STOP_TIMEOUT "$MOVER_STOP_TIMEOUT" acquire_lock detect_hosts [[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made" # ============================================================================================== # ━━━ Status ━━━ # ============================================================================================== if [[ "$SHOW_STATUS" == true ]]; then echo "" echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━" echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)" echo "$ICON_MOVER Timeout: ${MOVER_STOP_TIMEOUT}s" echo "$ICON_GEAR Dry Run: $DRY_RUN" echo "" if pgrep -f "emhttp.*Mover" >/dev/null 2>&1; then MOVER_PID=$(pgrep -f "emhttp.*Mover" | head -1) MOVER_START=$(ps -o lstart= -p "$MOVER_PID" 2>/dev/null | xargs) echo " $ICON_MOVER Mover: RUNNING (PID $MOVER_PID)" [[ -n "$MOVER_START" ]] && echo " $ICON_TIME Started: $MOVER_START" else echo " $ICON_MOVER Mover: not running" fi echo "━━━━━━━━━━━━━━━━━━━━━━━" exit 0 fi # ============================================================================================== # ━━━ Mover Stop ━━━ # ============================================================================================== START=$(date +%s) if ! pgrep -f "emhttp.*Mover" >/dev/null 2>&1; then log "Mover is not running — nothing to do" exit 0 fi MOVER_PID=$(pgrep -f "emhttp.*Mover" | head -1) warn "Mover is running (PID $MOVER_PID) — stopping in ${MOVER_STOP_TIMEOUT}s" # ── Warn users via wall ─────────────────────────────────────────────────────────────────────── if [[ "$DRY_RUN" == false ]]; then wall "$ICON_WARN $MY_ID ($LOCAL_SERVER_NAME) — unRAID Mover stopping in ${MOVER_STOP_TIMEOUT}s" log "Wall message sent — waiting ${MOVER_STOP_TIMEOUT}s..." sleep "$MOVER_STOP_TIMEOUT" else warn "DRY RUN — would send wall warning and wait ${MOVER_STOP_TIMEOUT}s" fi # ── SIGTERM — graceful stop ─────────────────────────────────────────────────────────────────── if [[ "$DRY_RUN" == true ]]; then warn "DRY RUN — would send SIGTERM to mover (PID $MOVER_PID)" else log "Sending SIGTERM to mover (PID $MOVER_PID)..." pkill -TERM -f "emhttp.*Mover" 2>/dev/null || true sleep 5 # Verify stopped after SIGTERM if ! pgrep -f "emhttp.*Mover" >/dev/null 2>&1; then warn "Mover stopped cleanly (SIGTERM) ✅" else # ── SIGKILL — forced stop ───────────────────────────────────────────────────────────── warn "Mover still running after SIGTERM — sending SIGKILL (may leave partial files)" pkill -KILL -f "emhttp.*Mover" 2>/dev/null || true sleep 2 # Final verify if pgrep -f "emhttp.*Mover" >/dev/null 2>&1; then error "Mover still running after SIGKILL — manual intervention needed" notify "Mover stop failed on $(hostname) ($MY_ID) — process unkillable" \ "Mover Stop" "warning" exit 1 else warn "Mover force-stopped (SIGKILL) — check for partial files on cache" fi fi fi END=$(date +%s) # ============================================================================================== # ━━━ Summary ━━━ # ============================================================================================== echo "" echo "━━━━━ $ICON_SUMMARY MOVER STOP SUMMARY ━━━━━" echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)" echo "$ICON_MOVER Timeout: ${MOVER_STOP_TIMEOUT}s" echo "$ICON_TIME Duration: $(format_duration $(( END - START )))" echo "" if [[ "$DRY_RUN" == true ]]; then warn "DRY RUN — no changes made" else log "$ICON_DONE Status: done — mover stopped ✅" fi echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"