Files
Varaverk/Plugin/unraid/System_Essentials/mover_stop.sh
T
Gmer4Lfe b00688bad1 Add RAG retrieval core, and correct six stale default values in headers
Chunker splits on the header sections the audit standardised, then sub-splits
named-paragraph safeguards — without that a specific question about one of
rsync.sh's fourteen safeguards scored below unrelated chunks, because the other
thirteen dominated the vector. Index is SQLite with raw float32 blobs and is
incremental on mtime; a no-op re-index takes 66ms.

The stale defaults were found by asking the system a question and checking its
answer: it correctly reported what mover_stop.sh's header claimed, and the
header was wrong.
2026-08-02 01:13:01 -04:00

217 lines
9.5 KiB
Bash
Executable File

#!/bin/bash
# ==============================================================================================
# ================================= Mover Stop =================================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Safely stops the unRAID mover with a wall warning, configurable timeout, and
# SIGTERM → SIGKILL sequence. Use before planned reboots, disk operations, or
# any operation where mover and rsync running simultaneously could corrupt files.
# Exits cleanly if mover is not running.
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
# Stop Sequence
# 1. Check if mover is running — exit cleanly if not
# 2. Wall message to all logged-in terminal users
# 3. Wait MOVER_STOP_TIMEOUT seconds
# 4. SIGTERM — allows mover to finish its current file before stopping
# (no partial files — the mover completes what it is working on)
# 5. Wait 5 seconds → verify stopped
# 6. SIGKILL if still running — forced stop, partial files possible
# 7. Final verify — error if still running after SIGKILL
#
# SIGTERM first because the mover has an opportunity to finish the file it is
# currently moving, leaving no partial copies on cache or array. SIGKILL is only
# used as a last resort and may leave a file split across cache and array.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Warn the Room First
# A wall message goes out before the mover is signalled. The mover moves other people's
# data between cache and array; anyone watching a transfer deserves to know why it stopped.
#
# Graceful First, Forced Last
# SIGTERM with a configurable window, then SIGKILL only if it is ignored. The mover is
# mid-file-move by definition — giving it the chance to finish the current file and exit
# cleanly is the difference between a stopped transfer and a half-moved file.
#
# Not Running Is Success
# An absent mover exits 0. Callers use this as a precondition ("ensure the mover is not
# running"), not as a command that must find something to kill — treating "already stopped"
# as failure would abort every reboot on a quiet system.
#
# Stop, Never Start
# This script has no counterpart that restarts the mover. Unraid's own schedule owns when
# the mover runs; this only ever removes it from the picture for a window.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Single Instance Lock
# acquire_lock prevents concurrent stop attempts racing each other.
#
# Root Required
# pkill on emhttp processes requires root.
#
# Final Verify
# Confirms mover is actually stopped after the kill sequence — errors if it
# is still running after SIGKILL.
#
# Silent When Clean
# Mover not running = log() only, no visible output.
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# master.conf
#
# MOVER_STOP_TIMEOUT
# Seconds between wall warning and SIGTERM. (shipped default: 300)
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# mover_stop.sh
# Check if mover is running. If so, warn users and stop it.
#
# mover_stop.sh --dry-run
# Show mover state and what would happen. No signals sent.
#
# mover_stop.sh --status
# Show mover state (running, PID, start time). Then exit.
#
# mover_stop.sh --log
# Verbose output showing each step of the stop sequence.
#
# ==============================================================================================
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
platform_require_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 platform_is_mover_running; then
MOVER_PID=$(platform_get_mover_pid)
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 ! platform_is_mover_running; then
echo "Mover is not running — nothing to do"
exit 0
fi
MOVER_PID=$(platform_get_mover_pid)
MOVER_START=$(ps -o lstart= -p "$MOVER_PID" 2>/dev/null | xargs)
MOVER_ELAPSED=$(ps -o etimes= -p "$MOVER_PID" 2>/dev/null | tr -d ' ')
warn "Mover is running (PID $MOVER_PID) — stopping in ${MOVER_STOP_TIMEOUT}s"
echo "$ICON_TIME Mover started: ${MOVER_START:-unknown} — running for $(format_duration "${MOVER_ELAPSED:-0}")"
# ── 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"
echo "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)..."
kill -TERM "$MOVER_PID" 2>/dev/null || true
sleep 5
# Verify stopped after SIGTERM
if ! platform_is_mover_running; then
warn "Mover stopped cleanly (SIGTERM) ✅"
else
# ── SIGKILL — forced stop ─────────────────────────────────────────────────────────────
warn "Mover still running after SIGTERM — sending SIGKILL (may leave partial files)"
kill -KILL "$MOVER_PID" 2>/dev/null || true
sleep 2
# Final verify
if platform_is_mover_running; 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
echo "$ICON_DONE Status: done — mover stopped ✅"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"