massive update. Master conf split, now modular with a load sceriprt to drive all configs to scripts. with unraid scpecific safeguard tests , and improved standardized ux. including dynamic host detect, who am i who else it there. EVERY SINGLE SCRIPT UPDATED. DEBATING THAT THIS IS ACUALLY V2

This commit is contained in:
2026-05-03 17:16:49 -04:00
parent 2691a35e80
commit ec7de648dc
72 changed files with 25640 additions and 14629 deletions
+260 -199
View File
@@ -1,135 +1,184 @@
#!/bin/bash
# -----------------------------------------------------------------------------------------------
# --------------------------------- Rsync Stop Script ------------------------------------------
# -----------------------------------------------------------------------------------------------
# Stops rsync intelligently — auto-detects what's running and acts accordingly.
# ==============================================================================================
# ================================= Rsync Stop =================================================
# ==============================================================================================
# Stops rsync intelligently on both local and remote servers.
# Auto-detects orchestrators and chooses the safest stop strategy automatically.
#
# Default behavior (just run it):
# Detects if an orchestrator (daily/weekly) is running
# If yes → kills rsync subprocess only
# orchestrator sees rsync died → moves to next share or exits cleanly
# If no → kills rsync processes directly (solo rsync.sh run)
# Cleans stale lock files
# Recovers any containers left stopped by interrupted rsync
# ── TWO MODES ─────────────────────────────────────────────────────────────────────────────────
# Default (smart):
# Detects if an orchestrator (daily/weekly/critical sync) is running
# If orchestrator found → kills rsync subprocess only
# Orchestrator sees rsync died → moves to next share or exits cleanly
# If no orchestrator → kills rsync directly (standalone rsync.sh run)
# Cleans stale lock files after kill
# Recovers containers left stopped by interrupted rsync (local only)
#
# --full-stop flag (nuclear):
# Kills orchestrator first → then rsync
# Use when: you want everything dead immediately
# daily/weekly loop will NOT continue to next share
# --full-stop (nuclear):
# Kills orchestrator first → then kills rsync
# Orchestrator will NOT continue to next share
# Use when: you need everything dead immediately
#
# Both local and remote are handled in one run.
# Remote containers left as-is — docker_watchdog.sh handles remote recovery.
# ── REMOTE HANDLING ───────────────────────────────────────────────────────────────────────────
# Both local and remote handled in one run via SSH.
# Remote containers left as-is — docker_watchdog.sh handles remote container recovery.
# If remote unreachable → skips remote cleanly, logs warning.
#
# Flags:
# (none) ← smart mode — auto-detects, rsync-only if orchestrator running
# --full-stop ← nuclear — kill orchestrator + rsync
# --dry-run ← preview without changes
# -----------------------------------------------------------------------------------------------
# ── ORCHESTRATOR DETECTION ────────────────────────────────────────────────────────────────────
# detect_rsync_parent() scans all lock files to find which running process
# has rsync as a descendant. No hardcoded list — works for any orchestrator.
# Returns: "script_name:parent_pid" if found, empty if rsync running standalone.
#
# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────
# Root check — pkill and docker require root
# acquire_lock — prevents concurrent stop attempts racing
# DOCKER_TIMEOUT — all docker calls protected against hung daemon
# SSH_TIMEOUT — all remote SSH calls timeout-protected
# SIGTERM → SIGKILL — graceful then forced for orchestrators
# Container recovery — restarts local containers left stopped by killed rsync
# validate_unraid_cmd — notify validated before use
#
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
# rsync_stop.sh — smart stop (auto-detect)
# rsync_stop.sh --full-stop — kill orchestrator + rsync
# rsync_stop.sh --rsync-only — skip container recovery (called by other scripts)
# rsync_stop.sh --dry-run — preview without changes
# rsync_stop.sh --status — show what's currently running
# rsync_stop.sh --full-stop --dry-run — preview full stop
# ==============================================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
source "$SCRIPT_DIR/../load_config.sh"
# Check for --full-stop before parse_args
DOCKER_TIMEOUT=15
SSH_TIMEOUT=15
# ── Parse special flags before parse_args ─────────────────────────────────────────────────────
FULL_STOP=false
RSYNC_ONLY_MODE=false
FILTERED_ARGS=()
for arg in "$@"; do
if [[ "$arg" == "--full-stop" ]]; then
FULL_STOP=true
else
FILTERED_ARGS+=("$arg")
fi
case "$arg" in
--full-stop) FULL_STOP=true ;;
--rsync-only) RSYNC_ONLY_MODE=true ;;
*) FILTERED_ARGS+=("$arg") ;;
esac
done
parse_args "${FILTERED_ARGS[@]}"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_GEAR Setup ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_GEAR Setup ━━━"
# ==============================================================================================
# ━━━ Setup ━━━
# ==============================================================================================
if [[ "$EUID" -ne 0 ]]; then
error "Must be run as root — pkill and docker require 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"
acquire_lock
detect_hosts
resolve_remote_ip
REMOTE_REACHABLE=true
if ! ping -c1 -W3 "$REMOTE_SERVER" &>/dev/null; then
warn "$ICON_PING Remote $REMOTE_SERVER_NAME unreachable — will skip remote"
REMOTE_REACHABLE=false
# Remote reachability
REMOTE_REACHABLE=false
if timeout "$SSH_TIMEOUT" ping -c1 -W3 "$REMOTE_SERVER" &>/dev/null; then
REMOTE_REACHABLE=true
log "$REMOTE_SERVER_NAME reachable ✅"
else
info "$ICON_PING $REMOTE_SERVER_NAME reachable"
warn "$REMOTE_SERVER_NAME unreachable — remote operations will be skipped"
fi
[[ "$SHOW_STATUS" == true ]] && show_status && exit 0
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
[[ "$FULL_STOP" == true ]] && warn "FULL STOP mode — orchestrator + rsync will be killed"
# ==============================================================================================
# ━━━ Status ━━━
# ==============================================================================================
if [[ "$SHOW_STATUS" == true ]]; then
echo ""
echo "━━━━━ $ICON_SUMMARY RSYNC STOP STATUS ━━━━━"
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
echo "$ICON_NET Remote: $REMOTE_ID ($REMOTE_SERVER_NAME)"
echo ""
LOCAL_PIDS=$(pgrep -x rsync 2>/dev/null | tr '\n' ' ')
echo " $ICON_SYNC Local rsync PIDs: ${LOCAL_PIDS:-none}"
for lockfile in "$LOCK_DIR"/*.lock; do
[[ -f "$lockfile" ]] || continue
content=$(cat "$lockfile" 2>/dev/null)
pid="${content%%:*}"
name="${content##*:}"
[[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null && \
echo " $ICON_RUNNING Lock: $name (PID $pid)"
done
if [[ "$REMOTE_REACHABLE" == true ]]; then
REMOTE_PIDS=$(timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \
-o ConnectTimeout="$SSH_TIMEOUT" \
root@"$REMOTE_SERVER" "pgrep -x rsync || true" 2>/dev/null | tr '\n' ' ')
echo " $ICON_SYNC Remote rsync PIDs: ${REMOTE_PIDS:-none}"
else
echo " $ICON_WARN Remote: unreachable"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━"
exit 0
fi
# ==============================================================================================
# ── ORCHESTRATOR DETECTION ────────────────────────────────────────────────────────────────────
# ==============================================================================================
# Scans lock files to find which running process has rsync as a descendant.
# No hardcoded script names — detects any orchestrator automatically.
# -----------------------------------------------------------------------------------------------
# ━━━ Auto-detect orchestrators ━━━
# Check if daily or weekly is running on local and remote
# This determines default behavior
# -----------------------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------------------
# detect_rsync_parent — scans all lock files, finds which running process has rsync as a child
# No hardcoded list — works for any orchestrator automatically
#
# Returns: "script_name:parent_pid" if found, empty if rsync running standalone
# -----------------------------------------------------------------------------------------------
detect_rsync_parent() {
local found=""
# Get all rsync PIDs running locally
local rsync_pids
rsync_pids=$(pgrep -x rsync 2>/dev/null || true)
[[ -z "$rsync_pids" ]] && echo "" && return
# Scan all lock files in LOCK_DIR
for lockfile in "$LOCK_DIR"/*.lock; do
[[ -f "$lockfile" ]] || continue
local content pid locked_name
content=$(cat "$lockfile" 2>/dev/null)
pid="${content%%:*}"
locked_name="${content##*:}"
# Skip if PID dead or is itself a rsync lock
[[ -z "$pid" ]] && continue
! kill -0 "$pid" 2>/dev/null && continue
[[ "$locked_name" == rsync_* ]] && continue
# Check if any rsync PID is a child of this lock's PID
local children
children=$(cat /proc/"$pid"/task/"$pid"/children 2>/dev/null || \
tr ' ' '\n' < /proc/"$pid"/children 2>/dev/null || true)
# Walk the child tree — rsync may be a grandchild (bash → rsync.sh → rsync)
local all_descendants
all_descendants=$(pgrep -P "$pid" 2>/dev/null || true)
# Check if any rsync PID is in the descendants
while IFS= read -r rsync_pid; do
[[ -z "$rsync_pid" ]] && continue
local ppid
ppid=$(awk '/^PPid:/{print $2}' /proc/"$rsync_pid"/status 2>/dev/null || echo "")
if echo "$all_descendants" | grep -qw "$rsync_pid" 2>/dev/null || \
[[ "$(cat /proc/"$rsync_pid"/status 2>/dev/null | awk '/^PPid:/{print $2}')" == "$pid" ]]; then
found="$locked_name:$pid"
break 2
[[ "$ppid" == "$pid" ]]; then
echo "${locked_name}:${pid}"
return
fi
done <<< "$rsync_pids"
done
echo "$found"
echo ""
}
detect_rsync_parent_remote() {
[[ "$REMOTE_REACHABLE" != true ]] && echo "" && return
# Run the same logic on remote via SSH
local found
found=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" bash << 'REMOTE_SCRIPT'
timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \
-o ConnectTimeout="$SSH_TIMEOUT" \
root@"$REMOTE_SERVER" bash << 'REMOTE_SCRIPT' 2>/dev/null
LOCK_DIR="/tmp/unraid_locks"
rsync_pids=$(pgrep -x rsync 2>/dev/null || true)
[[ -z "$rsync_pids" ]] && exit 0
for lockfile in "$LOCK_DIR"/*.lock; do
[[ -f "$lockfile" ]] || continue
content=$(cat "$lockfile" 2>/dev/null)
@@ -138,21 +187,18 @@ for lockfile in "$LOCK_DIR"/*.lock; do
[[ -z "$pid" ]] && continue
! kill -0 "$pid" 2>/dev/null && continue
[[ "$locked_name" == rsync_* ]] && continue
all_descendants=$(pgrep -P "$pid" 2>/dev/null || true)
while IFS= read -r rsync_pid; do
[[ -z "$rsync_pid" ]] && continue
ppid=$(awk '/^PPid:/{print $2}' /proc/"$rsync_pid"/status 2>/dev/null)
ppid=$(awk '/^PPid:/{print $2}' /proc/"$rsync_pid"/status 2>/dev/null || echo "")
if echo "$all_descendants" | grep -qw "$rsync_pid" 2>/dev/null || \
[[ "$ppid" == "$pid" ]]; then
echo "$locked_name:$pid"
echo "${locked_name}:${pid}"
exit 0
fi
done <<< "$rsync_pids"
done
REMOTE_SCRIPT
2>/dev/null)
echo "$found"
}
LOCAL_ORCH=$(detect_rsync_parent)
@@ -162,27 +208,26 @@ REMOTE_ORCH=""
# Determine mode
if [[ "$FULL_STOP" == true ]]; then
MODE="full-stop"
info "Mode: FULL STOP — orchestrator + rsync will be killed"
elif [[ -n "$LOCAL_ORCH" ]] || [[ -n "$REMOTE_ORCH" ]]; then
MODE="rsync-only"
[[ -n "$LOCAL_ORCH" ]] && info "Detected local orchestrator: ${LOCAL_ORCH%%:*} — rsync-only mode"
[[ -n "$REMOTE_ORCH" ]] && info "Detected remote orchestrator: ${REMOTE_ORCH%%:*} — rsync-only mode"
info "Orchestrator will continue after rsync is killed"
info "Use --full-stop to also kill the orchestrator"
[[ -n "$LOCAL_ORCH" ]] && \
warn "Local orchestrator detected: ${LOCAL_ORCH%%:*} — rsync-only mode"
[[ -n "$REMOTE_ORCH" ]] && \
warn "Remote orchestrator detected: ${REMOTE_ORCH%%:*} — rsync-only mode"
warn "Use --full-stop to also kill the orchestrator"
else
MODE="rsync-only"
info "No orchestrator detected — killing rsync directly"
log "No orchestrator detected — killing rsync directly"
fi
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_STOP Kill Orchestrators (full-stop only) ━━━
# -----------------------------------------------------------------------------------------------
# ==============================================================================================
# ── Kill Orchestrators (full-stop only) ───────────────────────────────────────────────────────
# ==============================================================================================
ORCHESTRATORS_KILLED=()
REMOTE_ORCHESTRATORS_KILLED=()
kill_orchestrator() {
local script_name="$1"
local pid="$2"
local script_name="$1" pid="$2"
local lockfile="$LOCK_DIR/${script_name}.lock"
if [[ "$DRY_RUN" == true ]]; then
@@ -196,7 +241,7 @@ kill_orchestrator() {
sleep 1
if ! kill -0 "$pid" 2>/dev/null; then
success "$script_name stopped ✅"
warn "$script_name stopped (PID $pid)"
rm -f "$lockfile"
return 0
else
@@ -207,70 +252,62 @@ kill_orchestrator() {
if [[ "$MODE" == "full-stop" ]]; then
echo ""
echo "━━━ $ICON_STOP Orchestrators ━━━"
echo "━━━ $ICON_STOP Kill Orchestrators ━━━"
# Local
if [[ -n "$LOCAL_ORCH" ]]; then
name="${LOCAL_ORCH%%:*}"
pid="${LOCAL_ORCH##*:}"
info "Killing local: $name (PID $pid)"
if kill_orchestrator "$name" "$pid"; then
ORCHESTRATORS_KILLED+=("$name")
fi
local_name="${LOCAL_ORCH%%:*}"
local_pid="${LOCAL_ORCH##*:}"
warn "Killing local: $local_name (PID $local_pid)"
kill_orchestrator "$local_name" "$local_pid" && \
ORCHESTRATORS_KILLED+=("$local_name")
else
info "No local orchestrator running"
log "No local orchestrator running"
fi
# Remote
if [[ "$REMOTE_REACHABLE" == true ]] && [[ -n "$REMOTE_ORCH" ]]; then
name="${REMOTE_ORCH%%:*}"
pid="${REMOTE_ORCH##*:}"
lockfile="$LOCK_DIR/${name}.lock"
info "Killing remote: $name (PID $pid)"
remote_name="${REMOTE_ORCH%%:*}"
remote_pid="${REMOTE_ORCH##*:}"
remote_lock="$LOCK_DIR/${remote_name}.lock"
warn "Killing remote: $remote_name (PID $remote_pid)"
if [[ "$DRY_RUN" == false ]]; then
ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
"kill -TERM '$pid' 2>/dev/null; sleep 2; \
kill -0 '$pid' 2>/dev/null && kill -KILL '$pid' 2>/dev/null; \
rm -f '$lockfile'" 2>/dev/null
success "Remote $name stopped ✅"
REMOTE_ORCHESTRATORS_KILLED+=("$name")
timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \
-o ConnectTimeout="$SSH_TIMEOUT" \
root@"$REMOTE_SERVER" \
"kill -TERM '$remote_pid' 2>/dev/null; sleep 2; \
kill -0 '$remote_pid' 2>/dev/null && kill -KILL '$remote_pid' 2>/dev/null; \
rm -f '$remote_lock'" 2>/dev/null
warn "Remote $remote_name stopped ✅"
REMOTE_ORCHESTRATORS_KILLED+=("$remote_name")
else
warn "DRY RUN — would kill remote $name (PID $pid)"
warn "DRY RUN — would kill remote $remote_name (PID $remote_pid)"
fi
elif [[ "$REMOTE_REACHABLE" == true ]]; then
info "No remote orchestrator running"
log "No remote orchestrator running"
fi
# Wait for subprocesses to settle
if [[ ${#ORCHESTRATORS_KILLED[@]} -gt 0 ]] || \
[[ ${#REMOTE_ORCHESTRATORS_KILLED[@]} -gt 0 ]]; then
info "Waiting 3s for subprocesses to settle..."
sleep 3
fi
[[ ${#ORCHESTRATORS_KILLED[@]} -gt 0 || \
${#REMOTE_ORCHESTRATORS_KILLED[@]} -gt 0 ]] && sleep 3
fi
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_STOP Local Rsync ━━━
# -----------------------------------------------------------------------------------------------
# ==============================================================================================
# ━━━ Local Rsync ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_STOP Local Rsync ━━━"
LOCAL_KILLED=false
LOCAL_PIDS=$(pgrep -x rsync || true)
LOCAL_PIDS=$(pgrep -x rsync 2>/dev/null || true)
if [[ -z "$LOCAL_PIDS" ]]; then
info "No rsync processes running locally"
log "No rsync processes running locally"
else
info "Found PIDs: $(echo "$LOCAL_PIDS" | tr '\n' ' ')"
warn "Found local rsync PIDs: $(echo "$LOCAL_PIDS" | tr '\n' ' ')"
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would kill local rsync"
else
if pkill -x rsync; then
success "Local rsync killed ✅"
LOCAL_KILLED=true
else
warn "pkill non-zero — may have already exited"
fi
pkill -x rsync 2>/dev/null && LOCAL_KILLED=true || \
warn "pkill returned non-zero — rsync may have already exited"
[[ "$LOCAL_KILLED" == true ]] && warn "Local rsync killed ✅"
fi
fi
@@ -280,61 +317,65 @@ for lockfile in "$LOCK_DIR"/rsync_*.lock; do
content=$(cat "$lockfile" 2>/dev/null)
pid="${content%%:*}"
if [[ -n "$pid" ]] && ! kill -0 "$pid" 2>/dev/null; then
info "Cleaning stale lock: $(basename "$lockfile")"
log "Cleaning stale lock: $(basename "$lockfile")"
[[ "$DRY_RUN" == false ]] && rm -f "$lockfile"
fi
done
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_STOP Remote Rsync ━━━
# -----------------------------------------------------------------------------------------------
# ==============================================================================================
# ━━━ Remote Rsync ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_STOP Remote Rsync ($REMOTE_SERVER_NAME) ━━━"
echo "━━━ $ICON_STOP Remote Rsync $REMOTE_SERVER_NAME ━━━"
REMOTE_KILLED=false
if [[ "$REMOTE_REACHABLE" == false ]]; then
warn "Skipping — $REMOTE_SERVER_NAME unreachable"
else
REMOTE_PIDS=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
"pgrep -x rsync || true" 2>/dev/null || true)
REMOTE_PIDS=$(timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \
-o ConnectTimeout="$SSH_TIMEOUT" \
root@"$REMOTE_SERVER" "pgrep -x rsync || true" 2>/dev/null || true)
if [[ -z "$REMOTE_PIDS" ]]; then
info "No rsync running on $REMOTE_SERVER_NAME"
log "No rsync running on $REMOTE_SERVER_NAME"
else
info "Found PIDs on $REMOTE_SERVER_NAME: $(echo "$REMOTE_PIDS" | tr '\n' ' ')"
warn "Found remote rsync PIDs: $(echo "$REMOTE_PIDS" | tr '\n' ' ')"
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would kill remote rsync"
else
if ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" "pkill -x rsync || true" 2>/dev/null; then
success "Remote rsync killed ✅"
REMOTE_KILLED=true
else
warn "Remote pkill non-zero — may have already exited"
fi
timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \
-o ConnectTimeout="$SSH_TIMEOUT" \
root@"$REMOTE_SERVER" "pkill -x rsync || true" 2>/dev/null && \
REMOTE_KILLED=true || \
warn "Remote pkill returned non-zero — rsync may have already exited"
[[ "$REMOTE_KILLED" == true ]] && warn "Remote rsync killed ✅"
fi
fi
fi
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_START $ICON_CONTAINERS Container Recovery ━━━
# Restart containers left stopped by interrupted rsync
# Only runs if something was actually killed locally
# Remote containers left as-is — docker_watchdog.sh handles remote
# -----------------------------------------------------------------------------------------------
# ==============================================================================================
# ━━━ Container Recovery ━━━
# ==============================================================================================
# Restart local containers left stopped by interrupted rsync.
# Remote containers left for docker_watchdog.sh to recover.
# Skipped with --rsync-only flag (called by other scripts that handle recovery themselves).
CONTAINERS_RESTARTED=()
CONTAINERS_FAILED=()
if [[ "$RSYNC_ONLY_MODE" == false ]] && \
{ [[ "$LOCAL_KILLED" == true ]] || [[ ${#ORCHESTRATORS_KILLED[@]} -gt 0 ]]; }; then
if [[ "$LOCAL_KILLED" == true ]] || [[ ${#ORCHESTRATORS_KILLED[@]} -gt 0 ]]; then
echo ""
echo "━━━ $ICON_START $ICON_CONTAINERS Container Recovery ━━━"
info "Checking all profile containers..."
echo "━━━ $ICON_START Container Recovery ━━━"
log "Checking profile containers for recovery..."
declare -A SEEN
ALL_CONTAINERS=()
for profile_containers in "${PROFILE_CRITICAL_CONTAINER_NAMES[@]}"; do
for profile_containers in "${PROFILE_CRITICAL_CONTAINER_NAMES[@]:-}"; do
read -r -a container_list <<< "$profile_containers"
for c in "${container_list[@]}"; do
for c in "${container_list[@]:-}"; do
[[ -z "$c" ]] && continue
if [[ -z "${SEEN[$c]:-}" ]]; then
SEEN[$c]=1
@@ -344,69 +385,89 @@ if [[ "$LOCAL_KILLED" == true ]] || [[ ${#ORCHESTRATORS_KILLED[@]} -gt 0 ]]; the
done
if [[ ${#ALL_CONTAINERS[@]} -eq 0 ]]; then
info "No containers defined — skipping recovery"
log "No profile containers defined — skipping recovery"
else
for c in "${ALL_CONTAINERS[@]}"; do
STATUS=$(docker inspect -f '{{.State.Running}}' "$c" 2>/dev/null || echo "unknown")
if [[ "$STATUS" == "true" ]]; then
info "$ICON_RUNNING $c — running ✅"
elif [[ "$STATUS" == "false" ]]; then
warn "$ICON_NOT_RUNNING $c — stopped, restarting..."
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would restart $c"
else
if docker start "$c" >/dev/null 2>&1; then
success "$c restarted ✅"
CONTAINERS_RESTARTED+=("$c")
STATUS=$(timeout "$DOCKER_TIMEOUT" docker inspect -f \
'{{.State.Running}}' "$c" 2>/dev/null || echo "unknown")
case "$STATUS" in
true)
log "$c — running ✅"
;;
false)
warn "$c — stopped — restarting..."
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would restart $c"
else
error "Failed to restart $c"
if timeout "$DOCKER_TIMEOUT" docker start "$c" >/dev/null 2>&1; then
warn "$c restarted ✅"
CONTAINERS_RESTARTED+=("$c")
else
error "Failed to restart $c"
CONTAINERS_FAILED+=("$c")
fi
fi
fi
else
info "$c not found on this host — skipping"
fi
;;
*)
log "$c not found locally — skipping"
;;
esac
done
fi
fi
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Summary ━━━
# -----------------------------------------------------------------------------------------------
# ==============================================================================================
# ━━━ Summary ━━━
# ==============================================================================================
echo ""
echo "━━━━━ $ICON_SUMMARY RSYNC STOP SUMMARY ━━━━━"
echo " Mode: $MODE"
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
echo "$ICON_GEAR Mode: $MODE"
echo ""
echo "$ICON_HOST Local ($LOCAL_SERVER_NAME):"
echo "$ICON_HOST Local ($MY_ID):"
[[ ${#ORCHESTRATORS_KILLED[@]} -gt 0 ]] && \
echo " $ICON_STOPPED Orchestrators killed: ${ORCHESTRATORS_KILLED[*]}"
[[ "$LOCAL_KILLED" == true ]] && \
echo " $ICON_STOPPED Rsync killed" || \
echo " $ICON_SUCCESS No rsync was running"
warn " Orchestrators killed: ${ORCHESTRATORS_KILLED[*]}"
if [[ "$LOCAL_KILLED" == true ]]; then
warn " Rsync killed ✅"
else
log " No rsync was running"
fi
echo "$ICON_NET Remote ($REMOTE_SERVER_NAME):"
echo "$ICON_NET Remote ($REMOTE_ID$REMOTE_SERVER_NAME):"
if [[ "$REMOTE_REACHABLE" == false ]]; then
echo " $ICON_WARN Unreachable — skipped"
warn " Unreachable — skipped"
else
[[ ${#REMOTE_ORCHESTRATORS_KILLED[@]} -gt 0 ]] && \
echo " $ICON_STOPPED Orchestrators killed: ${REMOTE_ORCHESTRATORS_KILLED[*]}"
[[ "$REMOTE_KILLED" == true ]] && \
echo " $ICON_STOPPED Rsync killed" || \
echo " $ICON_SUCCESS No rsync was running"
warn " Orchestrators killed: ${REMOTE_ORCHESTRATORS_KILLED[*]}"
if [[ "$REMOTE_KILLED" == true ]]; then
warn " Rsync killed ✅"
else
log " No rsync was running"
fi
fi
[[ ${#CONTAINERS_RESTARTED[@]} -gt 0 ]] && \
echo "$ICON_CONTAINERS Containers recovered: ${CONTAINERS_RESTARTED[*]}"
warn "$ICON_CONTAINERS Containers recovered: ${CONTAINERS_RESTARTED[*]}"
[[ ${#CONTAINERS_FAILED[@]} -gt 0 ]] && \
echo "$ICON_ERROR Containers failed to restart: ${CONTAINERS_FAILED[*]}"
echo ""
if [[ "$DRY_RUN" == true ]]; then
echo "$ICON_WARN Status: DRY RUN — no changes made"
warn "DRY RUN — no changes made"
else
echo "$ICON_DONE Status: $ICON_SUCCESS DONE"
log "$ICON_DONE Status: done ✅"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
if [[ "$LOCAL_KILLED" == true ]] || [[ "$REMOTE_KILLED" == true ]] || \
[[ ${#ORCHESTRATORS_KILLED[@]} -gt 0 ]]; then
notify "Rsync stopped on $(hostname) — mode: $MODE${#CONTAINERS_RESTARTED[@]} containers recovered" \
"Rsync Stop" "warning"
# Notify if anything was actually killed or failed
if [[ "$DRY_RUN" == false ]]; then
if [[ ${#CONTAINERS_FAILED[@]} -gt 0 ]]; then
notify "Rsync stop on $(hostname) ($MY_ID) — containers failed to restart: ${CONTAINERS_FAILED[*]}" \
"Rsync Stop" "warning"
elif [[ "$LOCAL_KILLED" == true || "$REMOTE_KILLED" == true || \
${#ORCHESTRATORS_KILLED[@]} -gt 0 ]]; then
notify "Rsync stopped on $(hostname) ($MY_ID) — mode: $MODE${CONTAINERS_RESTARTED:+ — recovered: ${CONTAINERS_RESTARTED[*]}}" \
"Rsync Stop" "warning"
fi
fi