Behavior fixes: - docker_daily_restart.sh: implement RESTART_VERIFY_WAIT sleep before verify_running() — variable was logged as config but never applied - cert_monitor.sh: --log now shows expiry date and days remaining for healthy domains in the per-domain summary; problems always show details Header corrections: - downloaders_reset.sh: fix frequency contradiction (15 min → 30 min) - lidarr_cleanup.sh: Emby scan triggers on actual deletions, not every run - ssh_setup.sh: document --local-only flag in USAGE section - zfs_memory_snapshot.sh: clarify dry-run skips log write in PURPOSE - ramdisk_stop.sh / ramdisk_setup.sh: document TRANSCODE_STATE_FILE var - smart_long_test.sh: remove SMART_TEMP_WARN/CRIT — not used in code
337 lines
13 KiB
Bash
Executable File
337 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
|
# ==============================================================================================
|
|
# ================================= Ramdisk Stop ===============================================
|
|
# ==============================================================================================
|
|
#
|
|
# PURPOSE
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Safely stops the transcode ramdisk: redirects the transcode symlink to the
|
|
# SSD fallback before unmounting so Emby continues writing without interruption,
|
|
# then unmounts the tmpfs and updates the state file.
|
|
#
|
|
# Primary use case: stopping the current ramdisk before re-running
|
|
# ramdisk_setup.sh with new size or threshold values (setup is idempotent —
|
|
# if the ramdisk is mounted, it skips the mount and reports status, so you
|
|
# must stop it first to change the size).
|
|
#
|
|
# ==============================================================================================
|
|
# OPERATIONAL MODEL
|
|
# ==============================================================================================
|
|
#
|
|
# Executes in safe order:
|
|
# 1. Validate — ramdisk mounted, SSD fallback exists
|
|
# 2. Redirect symlink → SSD (Emby immediately writes to SSD instead)
|
|
# 3. Warn if active transcode files still on ramdisk (informational — not a blocker)
|
|
# 4. Unmount ramdisk tmpfs
|
|
# 5. Update /tmp/transcode_state.db → current_target=TRANSCODE_SSD
|
|
#
|
|
# The symlink redirect happens before unmount so there is no window where Emby
|
|
# has nowhere to write. Existing in-progress transcode files on the ramdisk are
|
|
# lost on unmount — warn the user but proceed (this is expected for maintenance).
|
|
#
|
|
# ==============================================================================================
|
|
# OPERATIONAL SAFEGUARDS
|
|
# ==============================================================================================
|
|
#
|
|
# Root Required
|
|
# umount requires root.
|
|
#
|
|
# Single Instance Lock
|
|
# acquire_lock prevents concurrent stop attempts.
|
|
#
|
|
# Mounted Check
|
|
# Exits cleanly if ramdisk is not mounted — nothing to do.
|
|
#
|
|
# Symlink-First Order
|
|
# Symlink is redirected before unmount — Emby never sees a broken path.
|
|
#
|
|
# transcode_manager Warning
|
|
# Warns if transcode_manager.sh is running — it may flip the symlink back
|
|
# to ramdisk on its next cycle. Stop transcode_manager before running this
|
|
# if you need the SSD redirect to hold.
|
|
#
|
|
# ==============================================================================================
|
|
# CONFIGURATION
|
|
# ==============================================================================================
|
|
#
|
|
# host*.conf
|
|
# HOST*_TRANSCODE_SSD SSD fallback directory — redirect target during stop.
|
|
# Aliased by detect_hosts() → TRANSCODE_SSD.
|
|
#
|
|
# master.conf
|
|
# TRANSCODE_LINK Symlink Emby uses. Must match Emby's transcode path setting.
|
|
# RAMDISK_PATH tmpfs mount point.
|
|
# TRANSCODE_STATE_FILE Override state file path (default: ${STATE_DIR}/transcode_state.db).
|
|
#
|
|
# ==============================================================================================
|
|
# STATE FILES
|
|
# ==============================================================================================
|
|
#
|
|
# /tmp/transcode_state.db — updated to current_target=TRANSCODE_SSD after stop.
|
|
# transcode_manager.sh reads this on its next cycle.
|
|
#
|
|
# ==============================================================================================
|
|
# RUNTIME MODES
|
|
# ==============================================================================================
|
|
#
|
|
# ramdisk_stop.sh
|
|
# Stop the ramdisk: redirect symlink → SSD, unmount, update state.
|
|
#
|
|
# ramdisk_stop.sh --dry-run
|
|
# Show what would happen without making any changes.
|
|
#
|
|
# ramdisk_stop.sh --status
|
|
# Show current mount state, symlink target, active files on ramdisk. Exit.
|
|
#
|
|
# ramdisk_stop.sh --log
|
|
# Verbose output for each step.
|
|
#
|
|
# ==============================================================================================
|
|
|
|
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 — umount requires root"
|
|
exit 1
|
|
fi
|
|
|
|
|
|
acquire_lock
|
|
|
|
detect_hosts
|
|
|
|
STATE_FILE="${TRANSCODE_STATE_FILE:-${STATE_DIR:-/tmp}/transcode_state.db}"
|
|
|
|
log "Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
|
log "Ramdisk: $RAMDISK_PATH"
|
|
log "Fallback: $TRANSCODE_SSD"
|
|
log "Symlink: $TRANSCODE_LINK"
|
|
|
|
[[ "$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_RAM Ramdisk path: $RAMDISK_PATH"
|
|
echo "$ICON_DISK SSD fallback: $TRANSCODE_SSD"
|
|
echo "$ICON_LINK Symlink: $TRANSCODE_LINK"
|
|
echo ""
|
|
|
|
if mountpoint -q "$RAMDISK_PATH" 2>/dev/null; then
|
|
USAGE=$(df -BG "$RAMDISK_PATH" | awk 'NR==2 {print $3}')
|
|
AVAIL=$(df -BG "$RAMDISK_PATH" | awk 'NR==2 {print $4}')
|
|
echo " $ICON_RAM Ramdisk: mounted — $USAGE used / $AVAIL available ✅"
|
|
FILE_COUNT=$(find "$RAMDISK_PATH" -type f 2>/dev/null | wc -l)
|
|
echo " $ICON_RAM Active files on ramdisk: $FILE_COUNT"
|
|
else
|
|
echo " $ICON_RAM Ramdisk: NOT mounted"
|
|
fi
|
|
|
|
if [[ -L "$TRANSCODE_LINK" ]]; then
|
|
TARGET=$(readlink "$TRANSCODE_LINK")
|
|
echo " $ICON_LINK Symlink: $TRANSCODE_LINK → $TARGET"
|
|
else
|
|
echo " $ICON_LINK Symlink: not set"
|
|
fi
|
|
|
|
if [[ -f "$STATE_FILE" ]]; then
|
|
echo ""
|
|
echo " State file ($STATE_FILE):"
|
|
while IFS='=' read -r key value; do
|
|
[[ -z "$key" ]] && continue
|
|
echo " $key = $value"
|
|
done < "$STATE_FILE"
|
|
else
|
|
echo " $ICON_INFO State file: not found (ramdisk never started this boot)"
|
|
fi
|
|
|
|
if pgrep -f "transcode_manager.sh" >/dev/null 2>&1; then
|
|
echo ""
|
|
warn "transcode_manager.sh is currently RUNNING"
|
|
fi
|
|
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━"
|
|
exit 0
|
|
fi
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Preflight ━━━
|
|
# ==============================================================================================
|
|
echo ""
|
|
echo "━━━ $ICON_GEAR Preflight ━━━"
|
|
|
|
# Bail if not mounted — nothing to do
|
|
if ! mountpoint -q "$RAMDISK_PATH" 2>/dev/null; then
|
|
warn "Ramdisk is not mounted at $RAMDISK_PATH — nothing to stop"
|
|
exit 0
|
|
fi
|
|
|
|
log "Ramdisk is mounted ✅"
|
|
|
|
# Warn if transcode_manager is running — it may flip symlink back on next cycle
|
|
if pgrep -f "transcode_manager.sh" >/dev/null 2>&1; then
|
|
warn "transcode_manager.sh is currently RUNNING"
|
|
warn "It may flip the symlink back to ramdisk on its next cycle"
|
|
warn "Stop transcode_manager.sh first if you need the SSD redirect to hold"
|
|
echo ""
|
|
fi
|
|
|
|
# Confirm SSD fallback exists
|
|
if [[ ! -d "$TRANSCODE_SSD" ]]; then
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn "DRY RUN — SSD fallback does not exist: $TRANSCODE_SSD"
|
|
warn "DRY RUN — would create it before redirecting symlink"
|
|
else
|
|
warn "SSD fallback does not exist — creating: $TRANSCODE_SSD"
|
|
mkdir -p "$TRANSCODE_SSD" || {
|
|
error "Failed to create SSD fallback: $TRANSCODE_SSD"
|
|
error "Cannot safely redirect symlink — aborting"
|
|
exit 1
|
|
}
|
|
log "SSD fallback created ✅"
|
|
fi
|
|
else
|
|
log "SSD fallback exists: $TRANSCODE_SSD ✅"
|
|
fi
|
|
|
|
START=$(date +%s)
|
|
STOP_SUCCESS=true
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Redirect Symlink → SSD ━━━
|
|
# ==============================================================================================
|
|
# Redirect BEFORE unmount — Emby continues writing to SSD with no broken path window.
|
|
echo ""
|
|
echo "━━━ $ICON_LINK Redirect Symlink → SSD ━━━"
|
|
|
|
if [[ -L "$TRANSCODE_LINK" ]]; then
|
|
CURRENT_TARGET=$(readlink "$TRANSCODE_LINK")
|
|
if [[ "$CURRENT_TARGET" == "$TRANSCODE_SSD" ]]; then
|
|
echo "Symlink already points to SSD — no change needed ✅"
|
|
else
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn "DRY RUN — would redirect: $TRANSCODE_LINK → $TRANSCODE_SSD"
|
|
else
|
|
ln -sfn "$TRANSCODE_SSD" "$TRANSCODE_LINK" && \
|
|
warn "Symlink redirected: $TRANSCODE_LINK → $TRANSCODE_SSD ✅" || {
|
|
error "Failed to redirect symlink"
|
|
STOP_SUCCESS=false
|
|
}
|
|
fi
|
|
fi
|
|
elif [[ -e "$TRANSCODE_LINK" ]]; then
|
|
warn "$TRANSCODE_LINK exists but is not a symlink — leaving as-is"
|
|
else
|
|
warn "Symlink $TRANSCODE_LINK does not exist — nothing to redirect"
|
|
fi
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Active Files Warning ━━━
|
|
# ==============================================================================================
|
|
echo ""
|
|
echo "━━━ $ICON_RAM Active Files Check ━━━"
|
|
|
|
FILE_COUNT=$(find "$RAMDISK_PATH" -type f 2>/dev/null | wc -l)
|
|
if [[ "$FILE_COUNT" -gt 0 ]]; then
|
|
warn "⚠️ $FILE_COUNT file(s) still on ramdisk — will be lost on unmount"
|
|
warn "Active transcode sessions should be stopped before unmounting"
|
|
warn "Proceeding regardless (this is expected for maintenance)"
|
|
if [[ "$LOG" == true ]]; then
|
|
find "$RAMDISK_PATH" -type f 2>/dev/null | while read -r f; do
|
|
log " $f"
|
|
done
|
|
fi
|
|
else
|
|
log "No active files on ramdisk ✅"
|
|
fi
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Unmount ━━━
|
|
# ==============================================================================================
|
|
echo ""
|
|
echo "━━━ $ICON_RAM Unmount Ramdisk ━━━"
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn "DRY RUN — would unmount: $RAMDISK_PATH"
|
|
else
|
|
if umount "$RAMDISK_PATH" 2>/dev/null; then
|
|
warn "Ramdisk unmounted: $RAMDISK_PATH ✅"
|
|
else
|
|
# Regular unmount failed — check if only directory handles are open (no active writes)
|
|
OPEN_FILES=$(lsof +D "$RAMDISK_PATH" 2>/dev/null | awk 'NR>1 && $5 != "DIR"' | wc -l)
|
|
if [[ "$OPEN_FILES" -eq 0 ]]; then
|
|
warn "Busy — only directory handles open, no active writes — trying lazy unmount"
|
|
if umount -l "$RAMDISK_PATH"; then
|
|
warn "Ramdisk lazy-unmounted: $RAMDISK_PATH ✅"
|
|
warn "Handles will release when owning processes next check the directory"
|
|
else
|
|
error "Lazy unmount also failed — $RAMDISK_PATH"
|
|
STOP_SUCCESS=false
|
|
fi
|
|
else
|
|
error "Failed to unmount $RAMDISK_PATH — $OPEN_FILES file(s) still open for writing"
|
|
error "Stop active transcode sessions and retry"
|
|
STOP_SUCCESS=false
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Update State File ━━━
|
|
# ==============================================================================================
|
|
echo ""
|
|
echo "━━━ $ICON_GEAR State File ━━━"
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn "DRY RUN — would update $STATE_FILE: current_target=$TRANSCODE_SSD"
|
|
elif [[ "$STOP_SUCCESS" == true ]]; then
|
|
NOW=$(date +%s)
|
|
cat > "$STATE_FILE" <<EOF
|
|
current_target=$TRANSCODE_SSD
|
|
last_flip_time=$NOW
|
|
flip_count_hour=0
|
|
flip_hour_start=$NOW
|
|
EOF
|
|
log "State file updated: current_target=$TRANSCODE_SSD"
|
|
else
|
|
warn "Skipping state file update — stop had errors"
|
|
fi
|
|
|
|
END=$(date +%s)
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Summary ━━━
|
|
# ==============================================================================================
|
|
echo ""
|
|
echo "━━━━━ $ICON_SUMMARY RAMDISK STOP SUMMARY ━━━━━"
|
|
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
|
echo "$ICON_RAM Ramdisk: $RAMDISK_PATH"
|
|
echo "$ICON_DISK Fallback: $TRANSCODE_SSD"
|
|
echo "$ICON_LINK Symlink: $TRANSCODE_LINK → $(readlink "$TRANSCODE_LINK" 2>/dev/null || echo "not set")"
|
|
echo "$ICON_TIME Duration: $(format_duration $(( END - START )))"
|
|
echo ""
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn "DRY RUN — no changes made"
|
|
elif [[ "$STOP_SUCCESS" == true ]]; then
|
|
echo "$ICON_DONE Status: done ✅"
|
|
echo "Run ramdisk_setup.sh to remount with new configuration"
|
|
else
|
|
echo "$ICON_ERROR Status: STOP HAD ERRORS"
|
|
notify "Ramdisk stop errors on $(hostname) ($MY_ID) — check output" \
|
|
"Ramdisk Stop" "warning"
|
|
exit 1
|
|
fi
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|