Compare commits

...
3 Commits
21 changed files with 1570 additions and 1121 deletions
+2
View File
@@ -69,6 +69,8 @@
)
# ━━━ unRAID Essential Scripts ━━━
# clear_logs.sh system log file paths
LOG_FILES=(/var/log/syslog /var/log/messages /var/log/dmesg)
# unRAID reboot script user warning time (seconds)
REBOOT_SLEEP=300
# unRAID mover stop script timeout (seconds)
+214 -16
View File
@@ -1,23 +1,221 @@
This is the Rsync Setup Guide
This guide is a work in progress
This guide is a work in progress and generated by ai, dont have time for guides. so this works
Rsync Setup Guide
First git Repository need installed at /mnt/user/appdata/unraid_scripts/
Then made executable, copy and paste contents of git_pull_execute.sh script to a new script in unRAID User Scripts
Status: Work in Progress
For the unRAID Rsync Ecosystem — common.sh · Master.conf · rsync.sh · daily_sync.sh
in unRAID User Scripts, to use scripts in git
Create a new Script and name it whatever
You can copy contents of user_script_pluin.sh and paste then change acordingly
or
add a line pointing to script plus local folder to be synced
Exampl = /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-test/Arrs/
Overview
This opens the script and passes the folder over as an argument
This allows for multiple rsync ops running off a single core script
This guide walks through setting up the rsync ecosystem on both your primary and secondary unRAID 7.x servers. By the end you will have:
All changes made to 1 single core script then works for all user scripts that point to rsync.sh
SSH keys configured for server-to-server communication
Tailscale running on both servers for secure networking
Scripts scheduled and running via the User Scripts plugin
Automated daily sync of media shares and appdata profiles
Prerequisites
The Master.conf file is where all user changeable variables live, This is for all scripts
Both servers need the following before starting:
A common.sh file also is used to share all common codes that multiple scripts use
This allows for easier managment of scripts, editing, and what not
unRAID 7.x
User Scripts plugin installed via Community Applications
Tailscale plugin installed via Community Applications
Terminal access to both servers (via unRAID UI → Tools → Terminal, or SSH)
Step 1 — Tailscale Setup
Tailscale provides the secure network tunnel between your two servers. The scripts resolve the remote server's IP via Tailscale at runtime.
On Both Servers
Open Apps in the unRAID UI
Search for Tailscale and install the plugin
Once installed, go to Settings → Tailscale
Click Connect and authenticate with your Tailscale account
Verify both servers appear in your Tailscale admin console
Verify Connectivity
Run this on the primary to confirm it can see the secondary:
tailscale ip -4 unRAID-Jayred365
You should get back a 100.x.x.x IP. If not, check both servers in the Tailscale admin console.
Note: The hostnames used in Master.conf (HOST1 and HOST2) must match the Tailscale machine names exactly — these are case sensitive.
Step 2 — Generate SSH Keys (Server-to-Server)
The scripts use SSH keys for server-to-server rsync.
On Primary (unRAID-Gmer4Lfe):
ssh-keygen -t ed25519 -f /root/.ssh/Gmer4Lfe-rsync-key -C "gmer4lfe-rsync" -N ""
On Secondary (unRAID-Jayred365):
ssh-keygen -t ed25519 -f /root/.ssh/Jayred365-rsync-key -C "jayred365-rsync" -N ""
Copy Public Keys to Each Server
Primary → Secondary
# On primary
cat /root/.ssh/Gmer4Lfe-rsync-key.pub
Copy the output. Then on secondary:
mkdir -p /root/.ssh
echo "PASTE_PUBLIC_KEY_HERE" >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
Secondary → Primary
# On secondary
cat /root/.ssh/Jayred365-rsync-key.pub
Copy the output. Then on primary:
mkdir -p /root/.ssh
echo "PASTE_PUBLIC_KEY_HERE" >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
Test the Connection
From the primary, test SSH access to the secondary:
ssh -i /root/.ssh/Gmer4Lfe-rsync-key root@$(tailscale ip -4 unRAID-Jayred365) "echo connected"
You should see connected. If prompted for a password, recheck the key authorization.
Step 3 — Enable SSH on unRAID
unRAID 7.x has SSH disabled by default. Enable it on both servers so the scripts can connect:
Go to Settings → Management Access
Under Secure Shell, set SSH to Enabled
Set SSH port to 22 (default)
Click Apply
Security note: SSH is only exposed on your local network and Tailscale interface. The rsync scripts connect via Tailscale IP, so traffic is encrypted end-to-end.
Step 4 — Configure Master.conf
All user configuration lives in Master.conf. Open it and adjust the following to match your setup:
nano /mnt/user/appdata/unraid_scripts/Master.conf
Required Changes:
Variable Description Example
HOST1 Hostname of your primary server unRAID-Gmer4Lfe
HOST2 Hostname of your secondary server unRAID-Jayred365
HOST1_SSH_KEY Path to primary's rsync private key /root/.ssh/Gmer4Lfe-rsync-key
HOST2_SSH_KEY Path to secondary's rsync private key /root/.ssh/Jayred365-rsync-key
BW_LIMIT Global bandwidth limit in KB/s 12500
ROOTFS_WARN Remote rootfs % threshold before aborting 75
Daily Sync Shares
Add the full paths of all media shares you want synced nightly:
DAILY_SYNC_SHARES=(
/mnt/user/Movies
/mnt/user/Tv_Shows
/mnt/user/Music
# add more here
)
Profiles
Profiles control per-share rsync behaviour for your frequently synced appdata shares.
declare -A PROFILE_CRITICAL_CONTAINER_NAMES=(
[arrs_stack]="Sonarr Radarr Lidarr Prowlarr"
[important-data]="Postgres-NextCloud NextCloud"
)
Any share with no matching profile falls through to the global DEFAULT_RSYNC_OPTS.
Step 5 — Set Up User Scripts
The User Scripts plugin schedules and runs the scripts. Each sync job is its own script entry.
Frequent Sync Jobs
Create one script entry per appdata profile (Plugins → User Scripts → Add New Script)
Example for ARR stack:
#!/bin/bash
bash /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-Failover/Arrs_Stack
Daily Sync Orchestrator
#!/bin/bash
bash /mnt/user/appdata/unraid_scripts/Orchestrators/daily_sync.sh
Schedule daily at 01:00
Set scripts to run as Background Tasks
Step 6 — Verify the Setup
Manually test from the primary:
bash /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-Failover/Arrs_Stack --log
You should see a healthy run with:
━━━ ⚙️ Setup ━━━
️ [INFO] 🖥️ Host: unRAID-Gmer4Lfe → unRAID-Jayred365
️ [INFO] 🌐 Remote IP: 100.x.x.x
...
✅ [OK] All disks backing share are online
Step 7 — Secondary Server Initial Setup
If setting up secondary from scratch:
Complete Steps 15 on the secondary
Start the array and create your shares in the unRAID UI
Run the share recreation tool:
bash /mnt/user/appdata/unraid_scripts/Tools/recreate_shares.sh
Temporarily remove --delete from DEFAULT_RSYNC_OPTS for initial push
Restore --delete after the first run; next nightly run will clean .recovery files automatically
Troubleshooting
SSH connection refused
Verify SSH is enabled on target server (Step 3)
Check correct key referenced in Master.conf
Confirm Tailscale IP resolves
Pre-flight aborts
Rootfs above ROOTFS_WARN
Share exists but empty
Disk offline
Script not found
Verify scripts exist in /mnt/user/appdata/unraid_scripts/
Make scripts executable:
chmod +x /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh
chmod +x /mnt/user/appdata/unraid_scripts/Orchestrators/daily_sync.sh
chmod +x /mnt/user/appdata/unraid_scripts/Tools/recreate_shares.sh
Available Flags
Flag Description
--dry-run or -n Run without making changes
--log Enable verbose logging
--no-log Disable logging
--status Print resolved configuration and exit
Examples
# Preview what would be synced without transferring
bash rsync.sh /mnt/user/Movies --dry-run --log
# Check profile and settings resolved for a share
bash rsync.sh /mnt/user/appdata-Failover/Arrs_Stack --status
Repository Structure
Unraid_Scripts/
├── Master.conf # All user configuration — edit this file only
├── common.sh # Shared library — functions used by all scripts
├── Rsync/
│ └── rsync.sh # Core rsync script — called per share
├── Orchestrators/
│ └── daily_sync.sh # Daily media sync orchestrator
└── Tools/
└── recreate_shares.sh # Share directory recreation from cfg fil
-71
View File
@@ -1,71 +0,0 @@
!/bin/bash
# ----------------------------------------------------------------------------------------------
# ------------------------------------ Rsync command -------------------------------------------
# ----------------------------------------------------------------------------------------------
/mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-test/Arrs/
# ----------------------------------------------------------------------------------------------
# ------------------------------------------- Info ---------------------------------------------
# ----------------------------------------------------------------------------------------------
#
# These global configuartions can be added as arguments at the end of the script after folder location
#
# Its better to add profile in Master.conf for perm changes
#
# Profiles in Master.conf match the last string of folder location
#
# if a match is made with a list in Master.conf then the associated profile will be used
# If no match is made it defualts to global defualts
#-----------------------------------------------------------------------------------------------
#----------------------------- Arguments, add after rsync command ------------------------------
#-----------------------------------------------------------------------------------------------
# Does a dry run making no changes
# --dry-run
#
# Force logging
# --log)
#
# Force logging to be dissabled
# --no-log
#
# --help
#
# Example = /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-test/Arrs/ --dry-run --no-log
#-----------------------------------------------------------------------------------------------
#----------------- User Variables, Please adjust in Master.cong as needed ----------------------
#-----------------------------------------------------------------------------------------------
#
#
# Network speed limit (KB/s)
# BW_LIMIT=12500
#
# Retry logic
# RETRY_COUNT=3
#
# Sleep between retries (seconds)
# SLEEP=300
#
# Max number of concurrently running rsync processes
# MAX_RSYNC_PROCS=1
#
# Container start/stop/restart before and after rsync
# CRITICAL_CONTAINER_NAMES=()
#
# Containers that need delayed before starting
# DELAYED_CONTAINERS=()
#
# Delay in seconds between starting containers, useful for things like Authelia
# CONTAINER_DELAY=5
#
#-----------------------------------------------------------------------------------------------
#---------------End Of User Variables, Please adjust in Master.cong as needed ------------------
#-----------------------------------------------------------------------------------------------
#
# Examples
# /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-test/Arrs/ MAX_RSYNC_PROCS=2
# /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-test/Arrs/ --dry-run MAX_RSYNC_PROCS=2
#
# Copy and paste into unRAID User Script plugin
+110 -66
View File
@@ -1,6 +1,8 @@
#!/bin/bash
# -----------------------------------------------------------------------------------------------
# ------------------------- UNRAID OPS COMMON LIBRARY (v1.6) ------------------------------------
# ----------------- UNRAID OPS COMMON LIBRARY (STABLE FRAMEWORK v1) ----------------------------
# -----------------------------------------------------------------------------------------------
# Version: 2.1
# -----------------------------------------------------------------------------------------------
# Changelog:
# v1.0 — Initial stable framework
@@ -23,24 +25,32 @@
# ICON_NOT_RUNNING changed to ⭕ — distinct from ICON_STOPPED 🔴
# Section dividers updated from --- to ━━━ for cleaner log readability
# Summary passed/failed lines use ICON_SUCCESS and ICON_ERROR consistently
# v1.7 — ICON_MOVER added for mover operations
# ICON_CONTAINERS replaces ICON_DOCKER for docker/container operations
# validate_int added — reusable integer validation for any script
# v1.8 — ICON_PHP added for PHP-FPM operations
# v1.9 — ICON_REBOOT added for server reboot operations
# v2.0 — ICON_PLUGIN added for User Scripts plugin operations
# v2.1 — ICON_ZFS and ICON_MEM added for ZFS and memory diagnostics
# Diagnostics icon group added to icon block
# -----------------------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------------------
# ICONS
# Each icon has one job — do not reuse across different contexts.
# -----------------------------------------------------------------------------------------------
# System / Host
ICON_HOST="🖥️" # host detection
ICON_NET="🌐" # network / IP resolution
ICON_PING="📡" # connectivity check
ICON_GEAR="⚙️" # setup section header / profile load
# Health Checks
ICON_DISK="💾" # disk checks
ICON_HEALTH="🩺" # rootfs / share health checks
ICON_SHIELD="🛡️" # pre-flight section header
# Containers
ICON_CONTAINERS="📦" # container section anchor — paired with action icon for direction
ICON_STOP="⛔" # stop command being issued
@@ -49,23 +59,33 @@ ICON_START="▶️" # start command being issued
ICON_STARTED="💚" # container confirmed started
ICON_RUNNING="🟢" # container already running when checked
ICON_NOT_RUNNING="⭕" # container already stopped when checked — distinct from ICON_STOPPED
# Transfer
ICON_SYNC="🔄" # transfer section header
ICON_RUN="🚀" # sync starting / rsync attempt
ICON_RETRY="🔁" # retry attempt
ICON_DONE="🏁" # transfer complete
# Summary
ICON_SUMMARY="📋" # summary section header
ICON_TIME="⏱️" # duration line
# System Operations
ICON_MOVER="🔃" # mover operations
ICON_REBOOT="⚡" # server reboot operations
ICON_PLUGIN="🧩" # user scripts plugin operations
ICON_PHP="👥" # PHP-FPM operations
# Diagnostics
ICON_ZFS="📊" # ZFS ARC statistics
ICON_MEM="🧠" # memory status
# Output
ICON_INFO="️"
ICON_WARN="⚠️"
ICON_ERROR="❌"
ICON_SUCCESS="✅"
# -----------------------------------------------------------------------------------------------
# OUTPUT HELPERS
# Standardised output functions used across all scripts.
@@ -75,11 +95,11 @@ info() { echo "$ICON_INFO [INFO] $*"; }
warn() { echo "$ICON_WARN [WARN] $*"; }
error() { echo "$ICON_ERROR [ERROR] $*"; }
success() { echo "$ICON_SUCCESS [OK] $*"; }
log() {
[[ "${ENABLE_LOGGING:-false}" == true ]] && echo "[LOG] $*"
}
# -----------------------------------------------------------------------------------------------
# DURATION FORMATTER
# Converts raw seconds into a human readable string — e.g. 10m53s or 47s
@@ -92,7 +112,7 @@ format_duration() {
local rem=$((secs % 60))
[[ $mins -gt 0 ]] && echo "${mins}m${rem}s" || echo "${rem}s"
}
# -----------------------------------------------------------------------------------------------
# ARG PARSER
# Processes all flags and key=value pairs passed to any script.
@@ -105,14 +125,14 @@ parse_args() {
ENABLE_LOGGING=${ENABLE_LOGGING:-false}
DRY_RUN=${DRY_RUN:-false}
SHOW_STATUS=${SHOW_STATUS:-false}
CLEAN_ARGS=()
for ARG in "$@"; do
if [[ "$ARG" == *=* ]]; then
VAR="${ARG%%=*}"
VAL="${ARG#*=}"
case "$VAR" in
LOG)
[[ "$VAL" == "true" ]] && ENABLE_LOGGING=true
@@ -141,10 +161,10 @@ parse_args() {
esac
fi
done
PARSED_ARGS=("${CLEAN_ARGS[@]}")
}
# -----------------------------------------------------------------------------------------------
# VALIDATION
# Checks that a required variable is set and non-empty.
@@ -154,7 +174,31 @@ parse_args() {
require_var() {
[[ -z "${!1:-}" ]] && error "Missing required: $1" && exit 1
}
# -----------------------------------------------------------------------------------------------
# INTEGER VALIDATION
# Checks that a variable contains a valid positive integer.
# Exits with a clear error if the value is missing, empty, or not a number.
# Usage: validate_int VAR_NAME "$VAR_VALUE"
# Example: validate_int MOVER_STOP_TIMEOUT "$MOVER_STOP_TIMEOUT"
# -----------------------------------------------------------------------------------------------
validate_int() {
local name="$1"
local value="$2"
if [[ -z "$value" ]]; then
error "$name is not set — check Master.conf"
exit 1
fi
if ! [[ "$value" =~ ^[0-9]+$ ]]; then
error "$name must be a positive integer — got: '$value'"
exit 1
fi
log "$name validated: $value"
}
# -----------------------------------------------------------------------------------------------
# HOST DETECTION
# Determines local and remote server names by comparing hostname against HOST1/HOST2.
@@ -163,7 +207,7 @@ require_var() {
# -----------------------------------------------------------------------------------------------
detect_hosts() {
LOCAL_HOSTNAME="$(hostname)"
if [[ "$LOCAL_HOSTNAME" == "$HOST1" ]]; then
LOCAL_SERVER_NAME="$HOST1"
REMOTE_SERVER_NAME="$HOST2"
@@ -174,18 +218,18 @@ detect_hosts() {
error "Unknown host: $LOCAL_HOSTNAME"
exit 1
fi
declare -A SSH_KEYS
SSH_KEYS["$HOST1|$HOST2"]="$HOST1_SSH_KEY"
SSH_KEYS["$HOST2|$HOST1"]="$HOST2_SSH_KEY"
SSH_KEY="${SSH_KEYS[$LOCAL_SERVER_NAME|$REMOTE_SERVER_NAME]}"
[[ -z "$SSH_KEY" ]] && error "Missing SSH key mapping for $LOCAL_SERVER_NAME$REMOTE_SERVER_NAME" && exit 1
info "$ICON_HOST Host: $LOCAL_SERVER_NAME$REMOTE_SERVER_NAME"
}
# -----------------------------------------------------------------------------------------------
# REMOTE IP RESOLUTION
# Resolves the Tailscale IPv4 address of the remote server.
@@ -195,12 +239,12 @@ detect_hosts() {
resolve_remote_ip() {
log "Resolving remote IP for $REMOTE_SERVER_NAME..."
REMOTE_SERVER=$(tailscale ip -4 "$REMOTE_SERVER_NAME" 2>/dev/null)
[[ -z "$REMOTE_SERVER" ]] && error "Failed to resolve Tailscale IP for $REMOTE_SERVER_NAME" && exit 1
info "$ICON_NET Remote IP: $REMOTE_SERVER"
}
# -----------------------------------------------------------------------------------------------
# CONNECTIVITY CHECK
# Pings the remote server to confirm it is reachable before starting any transfers.
@@ -216,7 +260,7 @@ check_connectivity() {
fi
info "$ICON_PING $REMOTE_SERVER_NAME is reachable"
}
# -----------------------------------------------------------------------------------------------
# REMOTE ROOTFS SPACE CHECK
# Checks the remote server's rootfs usage before any rsync runs.
@@ -227,15 +271,15 @@ check_connectivity() {
# -----------------------------------------------------------------------------------------------
check_remote_rootfs() {
log "Checking remote rootfs usage..."
REMOTE_USAGE=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
"df / --output=pcent | tail -1 | tr -d ' %'" 2>/dev/null)
if [[ -z "$REMOTE_USAGE" ]]; then
error "Could not retrieve rootfs usage from $REMOTE_SERVER_NAME"
exit 1
fi
if [[ "$REMOTE_USAGE" -ge "${ROOTFS_WARN:-75}" ]]; then
echo ""
error "$ICON_HEALTH Remote rootfs is ${REMOTE_USAGE}% full — threshold is ${ROOTFS_WARN:-75}%"
@@ -244,10 +288,10 @@ check_remote_rootfs() {
echo ""
exit 1
fi
info "$ICON_HEALTH Remote rootfs: ${REMOTE_USAGE}% used (threshold: ${ROOTFS_WARN:-75}%)"
}
# -----------------------------------------------------------------------------------------------
# REMOTE SHARE VALIDATION
# Verifies that the target directory exists and is not empty on the remote server.
@@ -258,12 +302,12 @@ check_remote_rootfs() {
# -----------------------------------------------------------------------------------------------
check_remote_share() {
local dir="$1"
log "Checking remote share: $dir..."
SHARE_EXISTS=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
"[[ -d '$dir' ]] && echo yes || echo no" 2>/dev/null)
if [[ "$SHARE_EXISTS" != "yes" ]]; then
echo ""
error "$ICON_HEALTH Remote share does not exist: $dir"
@@ -272,10 +316,10 @@ check_remote_share() {
echo ""
exit 1
fi
SHARE_EMPTY=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
"[[ -z \"\$(ls -A '$dir' 2>/dev/null)\" ]] && echo yes || echo no" 2>/dev/null)
if [[ "$SHARE_EMPTY" == "yes" ]]; then
echo ""
warn "$ICON_HEALTH Remote share exists but is empty: $dir"
@@ -284,10 +328,10 @@ check_remote_share() {
echo ""
exit 1
fi
info "$ICON_HEALTH Remote share verified: $dir"
}
# -----------------------------------------------------------------------------------------------
# REMOTE DISK CHECK
# Verifies that all physical disks backing a share are online and mounted on the remote server.
@@ -301,12 +345,12 @@ check_remote_disks() {
local dir="$1"
local share_name
share_name=$(basename "$dir")
info "$ICON_DISK Checking disks backing $share_name on $REMOTE_SERVER_NAME..."
DISK_PATHS=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
"ls -d /mnt/disk*/$share_name 2>/dev/null" 2>/dev/null)
if [[ -z "$DISK_PATHS" ]]; then
echo ""
error "$ICON_DISK No disks found backing share $share_name on $REMOTE_SERVER_NAME"
@@ -315,18 +359,18 @@ check_remote_disks() {
echo ""
exit 1
fi
local all_ok=true
while IFS= read -r disk_share_path; do
local disk_mount
disk_mount=$(dirname "$disk_share_path")
local disk_name
disk_name=$(basename "$disk_mount")
MOUNTED=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
"mountpoint -q '$disk_mount' && echo yes || echo no" 2>/dev/null)
if [[ "$MOUNTED" == "yes" ]]; then
info "$ICON_DISK $disk_name $ICON_RUNNING$share_name present"
else
@@ -334,7 +378,7 @@ check_remote_disks() {
all_ok=false
fi
done <<< "$DISK_PATHS"
if [[ "$all_ok" == false ]]; then
echo ""
error "One or more disks backing $share_name are offline on $REMOTE_SERVER_NAME"
@@ -343,10 +387,10 @@ check_remote_disks() {
echo ""
exit 1
fi
success "All disks backing $share_name are online"
}
# -----------------------------------------------------------------------------------------------
# CONTAINER MANAGEMENT — STOP
# Stops all containers listed in CRITICAL_CONTAINER_NAMES on the remote server.
@@ -355,29 +399,29 @@ check_remote_disks() {
# CRITICAL_CONTAINER_NAMES must be a bash array — rsync.sh handles conversion from profile strings.
# -----------------------------------------------------------------------------------------------
RUNNING_CONTAINERS=()
stop_containers() {
if [[ ${#CRITICAL_CONTAINER_NAMES[@]} -eq 0 ]] || \
[[ "${CRITICAL_CONTAINER_NAMES[*]}" == "" ]]; then
log "No containers configured for this profile, skipping stop."
return
fi
info "Stopping containers..."
RUNNING_CONTAINERS=()
for c in "${CRITICAL_CONTAINER_NAMES[@]}"; do
[[ -z "$c" ]] && continue
info "Checking $c..."
STATUS=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
"docker inspect -f '{{.State.Running}}' $c 2>/dev/null" 2>/dev/null || echo "false")
if [[ "$STATUS" == "true" ]]; then
echo "$ICON_STOP Stopping $c..."
RUNNING_CONTAINERS+=("$c")
if ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" "docker stop $c" >/dev/null; then
echo "$ICON_STOPPED $c stopped"
else
@@ -388,7 +432,7 @@ stop_containers() {
fi
done
}
# -----------------------------------------------------------------------------------------------
# CONTAINER MANAGEMENT — START
# Restarts only the containers that were running before rsync and were stopped by stop_containers.
@@ -401,12 +445,12 @@ start_containers() {
log "No containers to restart."
return
fi
info "Starting containers..."
for c in "${RUNNING_CONTAINERS[@]}"; do
[[ -z "$c" ]] && continue
local needs_delay=false
for d in "${DELAYED_CONTAINERS[@]}"; do
if [[ "$c" == "$d" ]]; then
@@ -414,12 +458,12 @@ start_containers() {
break
fi
done
if [[ "$needs_delay" == true ]]; then
info "Waiting ${CONTAINER_DELAY}s before starting $c..."
sleep "$CONTAINER_DELAY"
fi
echo "$ICON_START Starting $c..."
if ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" "docker start $c" >/dev/null; then
echo "$ICON_STARTED $c started"
@@ -428,7 +472,7 @@ start_containers() {
fi
done
}
# -----------------------------------------------------------------------------------------------
# RSYNC OPTIONS
# Loads rsync options for the current profile from PROFILE_RSYNC_OPTS in Master.conf.
@@ -445,11 +489,11 @@ get_rsync_opts() {
log "Using default rsync opts: ${RSYNC_OPTS[*]}"
fi
}
# -----------------------------------------------------------------------------------------------
# STATUS DISPLAY
# Prints a summary of the current runtime configuration.
# Triggered by --status or --summary flag passed to rsync.sh.
# Triggered by --status or --summary flag passed to any script.
# Useful for verifying profile resolution and variable state before a live run.
# -----------------------------------------------------------------------------------------------
show_status() {
-122
View File
@@ -1,122 +0,0 @@
#!/bin/bash
set -e
# ----------------------------------------------------------------------------------------------
# ------------- Clear Unraid system logs and Docker container logs safely ----------------------
# ----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# STATUS MODE
if [[ "$SHOW_STATUS" == true ]]; then
echo
echo "=================================================="
echo " ️ LOG CLEANER STATUS"
echo "--------------------------------------------------"
echo " 📄 System Logs: ${LOG_FILES[*]}"
echo " 🐳 Docker Logs: /var/lib/docker/containers"
echo " 🧪 Dry Run: $DRY_RUN"
echo "=================================================="
exit 0
fi
# CONFIG
LOG_FILES=("${LOG_FILES[@]:-/var/log/syslog /var/log/messages /var/log/dmesg}")
# ROOT CHECK
if [[ "$EUID" -ne 0 ]]; then
error "🔴 Must be run as root"
fi
# HEADER
echo
echo "============================================================"
echo " 🚀 LOG CLEANER STARTING"
echo "------------------------------------------------------------"
echo " 📄 System Logs: ${LOG_FILES[*]}"
echo " 🐳 Docker Logs: enabled"
echo " 🧪 Dry Run: $DRY_RUN"
echo "============================================================"
log "️ Starting log cleanup"
# FUNCTIONS
clear_file() {
local file="$1"
if [[ ! -f "$file" ]]; then
warn "🟡 Not found: $file"
return
fi
if [[ "$DRY_RUN" == true ]]; then
warn "🧪 Would clear: $file"
else
: > "$file"
info "🟢 Cleared: $file"
fi
}
clear_docker_logs() {
if [[ ! -d /var/lib/docker/containers ]]; then
warn "🟡 Docker directory not found — skipping"
return
fi
local files
files=$(find /var/lib/docker/containers/ -name "*-json.log" 2>/dev/null || true)
if [[ -z "$files" ]]; then
warn "🟡 No Docker logs found"
return
fi
for file in $files; do
if [[ "$DRY_RUN" == true ]]; then
warn "🧪 Would clear Docker log: $file"
else
: > "$file"
info "🟢 Cleared Docker log: $file"
fi
done
}
# EXECUTION
START_TIME=$(date +%s)
for log in "${LOG_FILES[@]}"; do
clear_file "$log"
done
clear_docker_logs
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# SUMMARY
echo
echo "============================================================"
if [[ "$DRY_RUN" == true ]]; then
echo " 🟡 LOG CLEANER SUMMARY"
echo "------------------------------------------------------------"
echo " 📄 Logs: ${LOG_FILES[*]}"
echo " 🐳 Docker: skipped (dry run)"
echo " 📡 Status: 🟡 DRY RUN"
else
echo " 🟢 LOG CLEANER SUMMARY"
echo "------------------------------------------------------------"
echo " 📄 Logs: ${LOG_FILES[*]}"
echo " 🐳 Docker: cleaned"
echo " ⏱ Duration: ${DURATION}s"
echo " 📡 Status: 🟢 SUCCESS"
fi
echo "============================================================"
info "️ Log cleanup complete"
-125
View File
@@ -1,125 +0,0 @@
#!/bin/bash
set -e
# ----------------------------------------------------------------------------------------------
# ----------- Suppress noisy Docker veth/docker0 syslog messages on Unraid boot ----------------
# ----------------------------------------------------------------------------------------------
# ---------------- User Variables, Please adjust in Master.conf as needed ----------------------
# ----------------------------------------------------------------------------------------------
#
# User variables can be adjusted in Master.conf
#
# Scripts now just contain runtime logic
#
# This new setup allows for profiles and arguments to use the core script
# Use arguments in unRAID User Plugin for directory
# Example: /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh --dry-run
# ----------------------------------------------------------------------------------------------
# -------------- End Of User Variables, Please adjust in Master.conf as needed -----------------
# ----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# STATUS MODE
if [[ "$SHOW_STATUS" == true ]]; then
echo
echo "=================================================="
echo " ️ SYSLOG FILTER STATUS"
echo "--------------------------------------------------"
echo " 📄 Filter File: ${FILTER_FILE:-/etc/rsyslog.d/ignore-docker-veth.conf}"
echo " 🐳 Targets: veth, docker0"
echo " 🧪 Dry Run: $DRY_RUN"
echo "=================================================="
exit 0
fi
# CONFIG
FILTER_FILE=${FILTER_FILE:-"/etc/rsyslog.d/ignore-docker-veth.conf"}
# ROOT CHECK
if [[ "$EUID" -ne 0 ]]; then
error "🔴 Must be run as root"
fi
# HEADER
echo
echo "============================================================"
echo " 🚀 SYSLOG FILTER STARTING"
echo "------------------------------------------------------------"
echo " 📄 Filter File: $FILTER_FILE"
echo " 🐳 Targets: veth / docker0"
echo " 🧪 Dry Run: $DRY_RUN"
echo "============================================================"
log "️ Applying rsyslog docker noise filter"
# FUNCTIONS
create_filter() {
if [[ "$DRY_RUN" == true ]]; then
warn "🧪 Would create filter file: $FILTER_FILE"
return
fi
info "🟡 Creating rsyslog filter file"
cat <<'EOF' > "$FILTER_FILE"
if ($msg contains "veth" or $msg contains "docker0") then {
stop
}
EOF
info "🟢 Filter file written"
}
restart_rsyslog() {
if [[ "$DRY_RUN" == true ]]; then
warn "🧪 Would restart rsyslog service"
return
fi
info "🟡 Restarting rsyslog service"
if /etc/rc.d/rc.rsyslogd restart; then
info "🟢 rsyslog restarted successfully"
else
error "🔴 Failed to restart rsyslog"
fi
}
# EXECUTION
START_TIME=$(date +%s)
create_filter
restart_rsyslog
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# SUMMARY
echo
echo "============================================================"
if [[ "$DRY_RUN" == true ]]; then
echo " 🟡 SYSLOG FILTER SUMMARY"
echo "------------------------------------------------------------"
echo " 📄 Filter File: $FILTER_FILE"
echo " 🐳 Targets: veth / docker0"
echo " 📡 Status: 🟡 DRY RUN"
else
echo " 🟢 SYSLOG FILTER SUMMARY"
echo "------------------------------------------------------------"
echo " 📄 Filter File: $FILTER_FILE"
echo " 🐳 Targets: veth / docker0"
echo " ⏱ Duration: ${DURATION}s"
echo " 📡 Status: 🟢 SUCCESS"
fi
echo "============================================================"
info "️ Syslog filter operation complete"
-119
View File
@@ -1,119 +0,0 @@
#!/bin/bash
set -e
# ----------------------------------------------------------------------------------------------
# --------------------------- Mover Stop Script for Unraid -------------------------------------
# ----------------------------------------------------------------------------------------------
# ---------------- User Variables, Please adjust in Master.conf as needed ----------------------
# ----------------------------------------------------------------------------------------------
#
# User variables can be adjusted in Master.conf
#
# Scripts now just contain runtime logic
#
# This new setup allows for profiles and arguments to use the core script
# Use arguments in unRAID User Plugin for directory
# Example: /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh --dry-run
# ----------------------------------------------------------------------------------------------
# -------------- End Of User Variables, Please adjust in Master.conf as needed -----------------
# ----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# CONFIG
MOVER_STOP_TIMEOUT=${MOVER_STOP_TIMEOUT:-30}
validate_int MOVER_STOP_TIMEOUT "$MOVER_STOP_TIMEOUT"
# STATUS MODE
if [[ "$SHOW_STATUS" == true ]]; then
echo
echo "=================================================="
echo " ️ MOVER STOP STATUS"
echo "--------------------------------------------------"
echo " ⏱ Timeout: ${MOVER_STOP_TIMEOUT}s"
echo " 📡 Dry Run: $DRY_RUN"
echo "=================================================="
exit 0
fi
# HEADER
echo
echo "============================================================"
echo " 🚀 MOVER STOP STARTING"
echo "------------------------------------------------------------"
echo " ⏱ Timeout: ${MOVER_STOP_TIMEOUT}s"
echo " 🧪 Dry Run: $DRY_RUN"
echo "============================================================"
log "️ Checking mover status"
# FUNCTIONS
check_mover_running() {
pgrep -f "emhttp.*Mover" >/dev/null 2>&1
}
notify_users() {
warn "🟡 Notifying users: mover stopping soon"
wall "⚠️ Unraid Mover will stop in ${MOVER_STOP_TIMEOUT} second(s)."
}
stop_mover() {
if [[ "$DRY_RUN" == true ]]; then
warn "🧪 Would stop Unraid Mover process"
return
fi
info "🟡 Stopping Unraid Mover process"
if pkill -f "emhttp.*Mover"; then
info "🟢 Mover stopped successfully"
else
warn "🔴 Failed to stop mover (may not be running)"
fi
}
# EXECUTION
START_TIME=$(date +%s)
if check_mover_running; then
info "🟡 Mover is running"
notify_users
info "⏱ Waiting ${MOVER_STOP_TIMEOUT}s before stopping"
sleep "$MOVER_STOP_TIMEOUT"
stop_mover
else
info "🟢 Mover is not running"
fi
# FINALIZE
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# SUMMARY
echo
echo "============================================================"
if check_mover_running; then
echo " 🟡 MOVER STOP SUMMARY"
echo "------------------------------------------------------------"
echo " ⏱ Duration: ${DURATION}s"
echo " 📡 Status: 🟡 STILL RUNNING OR UNCERTAIN"
else
echo " 🟢 MOVER STOP SUMMARY"
echo "------------------------------------------------------------"
echo " ⏱ Duration: ${DURATION}s"
echo " 📡 Status: 🟢 STOPPED / NOT RUNNING"
fi
echo "============================================================"
info "️ Mover stop operation complete"
-135
View File
@@ -1,135 +0,0 @@
#!/bin/bash
set -e
# ----------------------------------------------------------------------------------------------
# ------------------ Persistently set PHP-FPM pm.max_children on Unraid ------------------------
# ----------------------------------------------------------------------------------------------
# ---------------- User Variables, Please adjust in Master.conf as needed ----------------------
# ----------------------------------------------------------------------------------------------
#
# User variables can be adjusted in Master.conf
#
# Scripts now just contain runtime logic
#
# This new setup allows for profiles and arguments to use the core script
# Use arguments in unRAID User Plugin for directory
# Example: /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh --dry-run
# ----------------------------------------------------------------------------------------------
# -------------- End Of User Variables, Please adjust in Master.conf as needed -----------------
# ----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# STATUS MODE
if [[ "$SHOW_STATUS" == true ]]; then
echo
echo "=================================================="
echo " ️ PHP-FPM CONFIG STATUS"
echo "--------------------------------------------------"
echo " ⚙️ PHP_CONF: $PHP_CONF"
echo " 👥 Max Children: ${PHP_MAX_CHILDREN:-unset}"
echo " 🧪 Dry Run: $DRY_RUN"
echo "=================================================="
exit 0
fi
# ROOT CHECK
if [[ "$EUID" -ne 0 ]]; then
error "🔴 Must be run as root"
fi
# VALIDATION
validate_int PHP_MAX_CHILDREN "$PHP_MAX_CHILDREN"
require_var PHP_CONF
# HEADER
echo
echo "============================================================"
echo " 🚀 PHP-FPM CONFIG UPDATE STARTING"
echo "------------------------------------------------------------"
echo " ⚙️ Target: $PHP_CONF"
echo " 👥 MaxChild: $PHP_MAX_CHILDREN"
echo " 🧪 Dry Run: $DRY_RUN"
echo "============================================================"
log "️ Starting PHP-FPM configuration update"
# FUNCTIONS
apply_php_max_children() {
local target="pm.max_children = $PHP_MAX_CHILDREN"
if [[ "$DRY_RUN" == true ]]; then
warn "🧪 Would set pm.max_children = $PHP_MAX_CHILDREN"
warn "🧪 Would restart PHP-FPM service"
return
fi
info "🟡 Applying PHP-FPM configuration"
if [[ ! -f "$PHP_CONF" ]]; then
error "🔴 PHP config file not found: $PHP_CONF"
fi
# Apply config safely
if sed -i "s/^pm\.max_children.*/$target/" "$PHP_CONF"; then
info "🟢 Config updated successfully"
else
error "🔴 Failed to update PHP config"
fi
# Restart service
info "🟡 Restarting PHP-FPM"
if /etc/rc.d/rc.php-fpm restart; then
info "🟢 PHP-FPM restarted successfully"
else
error "🔴 PHP-FPM restart failed"
fi
# Verify applied value
local current
current=$(grep -E "^pm\.max_children" "$PHP_CONF" || true)
if [[ -n "$current" ]]; then
info "🟢 Verified: $current"
logger "Userscript: PHP-FPM updated -> $current"
else
warn "🟡 Could not verify configuration value"
fi
}
# EXECUTION
START_TIME=$(date +%s)
apply_php_max_children
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# SUMMARY
echo
echo "============================================================"
if [[ "$DRY_RUN" == true ]]; then
echo " 🟡 PHP-FPM SUMMARY"
echo "------------------------------------------------------------"
echo " ⚙️ Target: $PHP_CONF"
echo " 👥 MaxChild: $PHP_MAX_CHILDREN"
echo " 📡 Status: 🟡 DRY RUN"
else
echo " 🟢 PHP-FPM SUMMARY"
echo "------------------------------------------------------------"
echo " ⚙️ Target: $PHP_CONF"
echo " 👥 MaxChild: $PHP_MAX_CHILDREN"
echo " ⏱ Duration: ${DURATION}s"
echo " 📡 Status: 🟢 SUCCESS"
fi
echo "============================================================"
info "️ PHP-FPM configuration update complete"
-109
View File
@@ -1,109 +0,0 @@
#!/bin/bash
set -e
# ----------------------------------------------------------------------------------------------
# ------------------------ Stop all running Rsync processes for Unraid -------------------------
# ----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
# ----------------------------- INIT -----------------------------
parse_args "$@"
require_var "HOST1"
require_var "HOST2"
detect_hosts
resolve_remote_ip
# ----------------------------- POSITIONAL MAP -----------------------------
SOURCE="${POSITIONAL_ARGS[0]:-}"
DEST="${POSITIONAL_ARGS[1]:-}"
# ----------------------------- STATUS MODE -----------------------------
if [[ "$SHOW_STATUS" == true ]]; then
show_status
exit 0
fi
# ----------------------------- HEADER -----------------------------
ui_header "RSYNC OPERATION"
ui_section "Context"
ui_kv "Source" "$SOURCE"
ui_kv "Destination" "$DEST"
ui_kv "Remote" "$REMOTE_SERVER_NAME"
ui_kv "IP" "$REMOTE_SERVER"
ui_kv "Dry Run" "$DRY_RUN"
# ----------------------------- VALIDATION -----------------------------
if [[ -z "$SOURCE" ]]; then
echo_error "Missing SOURCE argument"
exit 1
fi
if [[ -z "$DEST" ]]; then
echo_error "Missing DEST argument"
exit 1
fi
ui_status ok "Validation passed"
# ----------------------------- MIRROR MODE -----------------------------
if [[ "$SOURCE" == "$DEST" ]]; then
echo
echo "[INFO] Mirror mode detected (identical paths)"
echo "[INFO] Treating as cross-host sync of same structure"
log "Mirror mode active"
fi
# ----------------------------- PRECHECK -----------------------------
if ! check_remote_online; then
echo_error "Remote host offline"
exit 1
fi
echo "[OK] Remote reachable"
# ----------------------------- CONTAINERS -----------------------------
stop_containers
# ----------------------------- RSYNC EXECUTION -----------------------------
get_rsync_opts
RSYNC_CMD=(
rsync
"${RSYNC_OPTS[@]}"
"$SOURCE"
root@"$REMOTE_SERVER":"$DEST"
)
if [[ "$DRY_RUN" == true ]]; then
RSYNC_CMD+=("--dry-run")
fi
ui_section "Execution"
echo "[RUN] Starting rsync..."
log "CMD: ${RSYNC_CMD[*]}"
"${RSYNC_CMD[@]}"
# ----------------------------- RECOVERY -----------------------------
start_containers
# ----------------------------- END -----------------------------
ui_footer "RSYNC COMPLETE"
-152
View File
@@ -1,152 +0,0 @@
#!/bin/bash
set -e
# ----------------------------------------------------------------------------------------------
# ---------------------------- Unattended Reboot Script for Unraid -----------------------------
# ----------------------------------------------------------------------------------------------
# ---------------- User Variables, Please adjust in Master.conf as needed ----------------------
# ----------------------------------------------------------------------------------------------
#
# User variables can be adjusted in Master.conf
#
# Scripts now just contain runtime logic
#
# This new setup allows for profiles and arguments to use the core script
# Use arguments in unRAID User Plugin for directory
# Example: /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh --dry-run
# ----------------------------------------------------------------------------------------------
# -------------- End Of User Variables, Please adjust in Master.conf as needed -----------------
# ----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# STATUS MODE
if [[ "$SHOW_STATUS" == true ]]; then
echo
echo "=================================================="
echo " ️ REBOOT STATUS"
echo "--------------------------------------------------"
echo " ⏱ Delay: ${REBOOT_SLEEP:-0}s"
echo " 🧪 Dry Run: $DRY_RUN"
echo "=================================================="
exit 0
fi
# CONFIG
validate_int REBOOT_SLEEP "$REBOOT_SLEEP"
# HEADER
echo
echo "============================================================"
echo " 🚀 UNRAID REBOOT SEQUENCE STARTING"
echo "------------------------------------------------------------"
echo " ⏱ Delay: ${REBOOT_SLEEP}s"
echo " 🧪 Dry Run: $DRY_RUN"
echo "============================================================"
log "️ Reboot sequence initiated"
# FUNCTIONS
notify_users() {
warn "🟡 Notifying users of reboot"
wall "⚠️ Unraid server will reboot in ${REBOOT_SLEEP} second(s). Save your work."
}
stop_docker() {
if [[ "$DRY_RUN" == true ]]; then
warn "🧪 Would stop Docker service"
return
fi
info "🟡 Stopping Docker service"
if /etc/rc.d/rc.docker stop; then
info "🟢 Docker stopped"
else
warn "🔴 Docker stop failed or already stopped"
fi
}
stop_vm_manager() {
if [[ "$DRY_RUN" == true ]]; then
warn "🧪 Would stop VM Manager (libvirt)"
return
fi
info "🟡 Stopping VM Manager"
if /etc/rc.d/rc.libvirt stop; then
info "🟢 VM Manager stopped"
else
warn "🔴 VM Manager stop failed or already stopped"
fi
}
sync_disks() {
if [[ "$DRY_RUN" == true ]]; then
warn "🧪 Would sync filesystem buffers"
return
fi
info "🟡 Syncing disks"
if sync; then
info "🟢 Disk sync complete"
else
warn "🔴 Sync command returned error"
fi
}
reboot_system() {
if [[ "$DRY_RUN" == true ]]; then
warn "🧪 Would reboot system (/sbin/reboot)"
return
fi
info "🔴 Rebooting system NOW"
/sbin/reboot
}
# EXECUTION
START_TIME=$(date +%s)
if [[ "$REBOOT_SLEEP" -gt 0 ]]; then
notify_users
info "⏱ Waiting ${REBOOT_SLEEP}s before shutdown sequence"
sleep "$REBOOT_SLEEP"
fi
stop_docker
stop_vm_manager
sync_disks
reboot_system
# NOTE: system will not reach here unless dry-run
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# SUMMARY
echo
echo "============================================================"
if [[ "$DRY_RUN" == true ]]; then
echo " 🟡 REBOOT SUMMARY"
echo "------------------------------------------------------------"
echo " ⏱ Delay: ${REBOOT_SLEEP}s"
echo " 📡 Status: 🟡 DRY RUN (no reboot executed)"
else
echo " 🔴 REBOOT SUMMARY"
echo "------------------------------------------------------------"
echo " ⚠️ Status: 🔴 SYSTEM SHOULD BE REBOOTING"
echo " ⏱ Duration: ${DURATION}s"
fi
echo "============================================================"
info "️ Reboot sequence complete (or handed off to system reboot)"
-114
View File
@@ -1,114 +0,0 @@
#!/bin/bash
set -e
# ----------------------------------------------------------------------------------------------
# ----------------- Stop all running User Scripts spawned by the User Scripts plugin -----------
# ----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# STATUS MODE
if [[ "$SHOW_STATUS" == true ]]; then
echo
echo "=================================================="
echo " ️ USER SCRIPTS STATUS"
echo "--------------------------------------------------"
echo " 🔍 Target: /tmp/user.scripts processes"
echo " 🧪 Dry Run: $DRY_RUN"
echo "=================================================="
exit 0
fi
# HEADER
echo
echo "============================================================"
echo " 🚀 USER SCRIPTS STOP STARTING"
echo "------------------------------------------------------------"
echo " 🔍 Target: User Scripts Plugin processes"
echo " 🧪 Dry Run: $DRY_RUN"
echo "============================================================"
log "️ Scanning for User Scripts processes"
# FUNCTIONS
get_user_script_pids() {
/usr/bin/ps -eo pid,cmd | grep "/tmp/user.scripts" | grep -v grep | awk '{print $1}'
}
stop_user_scripts() {
local pids
pids=$(get_user_script_pids)
if [[ -z "$pids" ]]; then
info "🟢 No running User Scripts found"
return
fi
local count=0
for pid in $pids; do
if [[ "$DRY_RUN" == true ]]; then
warn "🧪 Would kill User Script PID $pid"
else
info "🟡 Killing User Script PID $pid"
if kill "$pid" 2>/dev/null; then
info "🟢 Killed PID $pid"
else
warn "🔴 Failed to kill PID $pid (may already be gone)"
fi
fi
count=$((count + 1))
done
if [[ "$DRY_RUN" == true ]]; then
warn "🧪 Would stop $count User Script process(es)"
else
info "🟢 Process cleanup complete ($count process(es) targeted)"
fi
}
# EXECUTION
START_TIME=$(date +%s)
stop_user_scripts
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# SUMMARY
echo
echo "============================================================"
if [[ "$DRY_RUN" == true ]]; then
echo " 🟡 USER SCRIPTS SUMMARY"
echo "------------------------------------------------------------"
echo " 🔍 Status: 🟡 DRY RUN (no processes killed)"
echo " ⏱ Duration: ${DURATION}s"
else
local remaining
remaining=$(get_user_script_pids)
if [[ -z "$remaining" ]]; then
echo " 🟢 USER SCRIPTS SUMMARY"
echo "------------------------------------------------------------"
echo " 🔍 Status: 🟢 ALL PROCESSES STOPPED"
else
echo " 🟡 USER SCRIPTS SUMMARY"
echo "------------------------------------------------------------"
echo " 🔍 Status: 🟡 SOME PROCESSES MAY STILL BE RUNNING"
fi
echo " ⏱ Duration: ${DURATION}s"
fi
echo "============================================================"
info "️ User Scripts stop operation complete"
-92
View File
@@ -1,92 +0,0 @@
#!/bin/bash
set -e
# ----------------------------------------------------------------------------------------------
# --------------------------- ZFS ARC & Memory Snapshot for Unraid -----------------------------
# ----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# STATUS MODE
if [[ "$SHOW_STATUS" == true ]]; then
echo
echo "=================================================="
echo " ️ ZFS + MEMORY SNAPSHOT STATUS"
echo "--------------------------------------------------"
echo " 📊 Mode: Read-only diagnostics"
echo " 🧪 Dry Run: $DRY_RUN"
echo "=================================================="
exit 0
fi
# HEADER
echo
echo "============================================================"
echo " 📊 ZFS ARC + MEMORY SNAPSHOT"
echo "------------------------------------------------------------"
echo " 🕒 Time: $(date)"
echo " 🧪 Dry Run: $DRY_RUN"
echo "============================================================"
log "️ Collecting ZFS ARC + memory statistics"
# FUNCTIONS
show_zfs_arc() {
echo
echo "==================== ZFS ARC STATS ===================="
if [[ ! -r /proc/spl/kstat/zfs/arcstats ]]; then
warn "🔴 ZFS arcstats not available on this system"
return
fi
grep -iE '^(c|size|hits|misses|arc_meta_used|demand_metadata_misses|mru_ghost_metadata|mfu_ghost_metadata)' \
/proc/spl/kstat/zfs/arcstats 2>/dev/null || warn "🟡 Unable to read ARC stats"
}
show_memory_status() {
echo
echo "==================== MEMORY STATUS ===================="
if command -v free >/dev/null 2>&1; then
free -h | awk '
NR==1 {print $0}
/Mem:/ {print $0}
/Swap:/ {print $0}'
else
warn "🔴 free command not available"
fi
}
# EXECUTION
START_TIME=$(date +%s)
if [[ "$DRY_RUN" == true ]]; then
warn "🧪 Dry run enabled - no system data collected"
else
show_zfs_arc
show_memory_status
fi
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# SUMMARY
echo
echo "============================================================"
echo " 📊 SNAPSHOT SUMMARY"
echo "------------------------------------------------------------"
echo " ⏱ Duration: ${DURATION}s"
echo " 📡 Mode: READ-ONLY"
echo " 🧠 Source: ZFS ARC + system memory"
echo "============================================================"
info "️ Snapshot complete"
+129
View File
@@ -0,0 +1,129 @@
#!/bin/bash
# -----------------------------------------------------------------------------------------------
# --------------------------------- Clear Logs Script ------------------------------------------
# -----------------------------------------------------------------------------------------------
# Clears unRAID system logs and Docker container logs safely.
# Log file paths are configured in Master.conf under LOG_FILES.
# Supports --dry-run to preview what would be cleared without making changes.
# -----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_GEAR Setup ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_GEAR Setup ━━━"
# ROOT CHECK
if [[ "$EUID" -ne 0 ]]; then
error "Must be run as root"
exit 1
fi
success "Running as root"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Status ━━━
# -----------------------------------------------------------------------------------------------
if [[ "$SHOW_STATUS" == true ]]; then
echo ""
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
echo "$ICON_HEALTH System Logs: ${LOG_FILES[*]}"
echo "$ICON_DOCKER Docker Logs: /var/lib/docker/containers"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo "━━━━━━━━━━━━━━━━━━━━━━━"
exit 0
fi
# -----------------------------------------------------------------------------------------------
# FUNCTIONS
# -----------------------------------------------------------------------------------------------
# Clears a single log file if it exists.
# Skips with a warning if the file is not found.
clear_file() {
local file="$1"
if [[ ! -f "$file" ]]; then
warn "Not found: $file — skipping"
return
fi
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would clear: $file"
else
: > "$file"
success "Cleared: $file"
fi
}
# Finds and clears all Docker container json log files.
# Skips gracefully if Docker directory or log files are not found.
clear_docker_logs() {
if [[ ! -d /var/lib/docker/containers ]]; then
warn "$ICON_DOCKER Docker directory not found — skipping"
return
fi
local files
files=$(find /var/lib/docker/containers/ -name "*-json.log" 2>/dev/null || true)
if [[ -z "$files" ]]; then
warn "$ICON_DOCKER No Docker logs found — skipping"
return
fi
while IFS= read -r file; do
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would clear Docker log: $file"
else
: > "$file"
success "Cleared Docker log: $(basename "$(dirname "$file")")"
fi
done <<< "$files"
}
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_HEALTH Clear Logs ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_HEALTH Clear Logs ━━━"
echo "$ICON_HEALTH System Logs: ${LOG_FILES[*]}"
echo "$ICON_DOCKER Docker Logs: enabled"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo ""
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
START=$(date +%s)
for logfile in "${LOG_FILES[@]}"; do
clear_file "$logfile"
done
echo ""
echo "━━━ $ICON_DOCKER Docker ━━━"
clear_docker_logs
END=$(date +%s)
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Summary ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━━━ $ICON_SUMMARY LOG CLEANER SUMMARY ━━━━━"
echo "$ICON_HEALTH System Logs: ${LOG_FILES[*]}"
echo "$ICON_DOCKER Docker Logs: $([[ "$DRY_RUN" == true ]] && echo "skipped (dry run)" || echo "cleared")"
echo "$ICON_TIME Duration: $(format_duration $((END - START)))"
if [[ "$DRY_RUN" == true ]]; then
echo "$ICON_WARN Status: DRY RUN — no changes made"
else
echo "$ICON_DONE Status: $ICON_SUCCESS DONE"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+118
View File
@@ -0,0 +1,118 @@
#!/bin/bash
# -----------------------------------------------------------------------------------------------
# --------------------------------- Docker Syslog Filter ---------------------------------------
# -----------------------------------------------------------------------------------------------
# Suppresses noisy Docker veth/docker0 syslog messages on unRAID boot.
# Creates an rsyslog filter file and restarts the rsyslog service.
# Filter file path is configured in Master.conf under FILTER_FILE.
# Supports --dry-run to preview what would be done without making changes.
# -----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_GEAR Setup ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_GEAR Setup ━━━"
# ROOT CHECK
if [[ "$EUID" -ne 0 ]]; then
error "Must be run as root"
exit 1
fi
success "Running as root"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Status ━━━
# -----------------------------------------------------------------------------------------------
if [[ "$SHOW_STATUS" == true ]]; then
echo ""
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
echo "$ICON_HEALTH Filter File: $FILTER_FILE"
echo "$ICON_CONTAINERS Targets: veth, docker0"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo "━━━━━━━━━━━━━━━━━━━━━━━"
exit 0
fi
# -----------------------------------------------------------------------------------------------
# FUNCTIONS
# -----------------------------------------------------------------------------------------------
# Creates the rsyslog filter file that suppresses veth and docker0 noise.
# Filter is written to FILTER_FILE defined in Master.conf.
create_filter() {
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would create filter file: $FILTER_FILE"
return
fi
info "Writing rsyslog filter file: $FILTER_FILE"
cat <<'EOF' > "$FILTER_FILE"
if ($msg contains "veth" or $msg contains "docker0") then {
stop
}
EOF
success "Filter file written"
}
# Restarts the rsyslog service to apply the new filter.
# Uses unRAID's native rc.rsyslogd script.
restart_rsyslog() {
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would restart rsyslog service"
return
fi
info "Restarting rsyslog..."
if /etc/rc.d/rc.rsyslogd restart; then
success "rsyslog restarted"
else
error "Failed to restart rsyslog"
exit 1
fi
}
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_HEALTH Syslog Filter ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_HEALTH Syslog Filter ━━━"
echo "$ICON_HEALTH Filter File: $FILTER_FILE"
echo "$ICON_CONTAINERS Targets: veth / docker0"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo ""
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
START=$(date +%s)
create_filter
restart_rsyslog
END=$(date +%s)
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Summary ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━━━ $ICON_SUMMARY SYSLOG FILTER SUMMARY ━━━━━"
echo "$ICON_HEALTH Filter File: $FILTER_FILE"
echo "$ICON_CONTAINERS Targets: veth / docker0"
echo "$ICON_TIME Duration: $(format_duration $((END - START)))"
if [[ "$DRY_RUN" == true ]]; then
echo "$ICON_WARN Status: DRY RUN — no changes made"
else
echo "$ICON_DONE Status: $ICON_SUCCESS DONE"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+114
View File
@@ -0,0 +1,114 @@
#!/bin/bash
# -----------------------------------------------------------------------------------------------
# --------------------------------- Mover Stop Script ------------------------------------------
# -----------------------------------------------------------------------------------------------
# Safely stops the unRAID mover process with a user warning before halting.
# Timeout before stopping is configured in Master.conf under MOVER_STOP_TIMEOUT.
# Supports --dry-run to preview what would happen without making changes.
# -----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_GEAR Setup ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_GEAR Setup ━━━"
# ROOT CHECK
if [[ "$EUID" -ne 0 ]]; then
error "Must be run as root"
exit 1
fi
success "Running as root"
# Validate MOVER_STOP_TIMEOUT is a valid integer before using it
validate_int MOVER_STOP_TIMEOUT "$MOVER_STOP_TIMEOUT"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Status ━━━
# -----------------------------------------------------------------------------------------------
if [[ "$SHOW_STATUS" == true ]]; then
echo ""
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
echo "$ICON_MOVER Timeout: ${MOVER_STOP_TIMEOUT}s"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo "━━━━━━━━━━━━━━━━━━━━━━━"
exit 0
fi
# -----------------------------------------------------------------------------------------------
# FUNCTIONS
# -----------------------------------------------------------------------------------------------
# Returns 0 if the unRAID mover process is currently running, 1 if not.
check_mover_running() {
pgrep -f "emhttp.*Mover" >/dev/null 2>&1
}
# Broadcasts a wall message to all logged in users warning mover is stopping.
notify_users() {
warn "Notifying users — mover stopping in ${MOVER_STOP_TIMEOUT}s"
wall "$ICON_WARN unRAID Mover will stop in ${MOVER_STOP_TIMEOUT} second(s)."
}
# Sends SIGTERM to the mover process via pkill.
# Skips if dry run is active.
stop_mover() {
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would stop unRAID Mover process"
return
fi
info "Stopping unRAID Mover..."
if pkill -f "emhttp.*Mover"; then
success "Mover stopped"
else
warn "Could not stop mover — may have already stopped"
fi
}
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_MOVER Mover Stop ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_MOVER Mover Stop ━━━"
echo "$ICON_MOVER Timeout: ${MOVER_STOP_TIMEOUT}s"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo ""
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
START=$(date +%s)
if check_mover_running; then
info "$ICON_MOVER Mover is running"
notify_users
info "Waiting ${MOVER_STOP_TIMEOUT}s before stopping..."
sleep "$MOVER_STOP_TIMEOUT"
stop_mover
else
info "$ICON_MOVER Mover is not running — nothing to do"
fi
END=$(date +%s)
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Summary ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━━━ $ICON_SUMMARY MOVER STOP SUMMARY ━━━━━"
echo "$ICON_TIME Duration: $(format_duration $((END - START)))"
if check_mover_running; then
echo "$ICON_ERROR Status: $ICON_ERROR STILL RUNNING"
else
echo "$ICON_DONE Status: $ICON_SUCCESS STOPPED / NOT RUNNING"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+131
View File
@@ -0,0 +1,131 @@
#!/bin/bash
# -----------------------------------------------------------------------------------------------
# ----------------------------- PHP-FPM Max Children Script ------------------------------------
# -----------------------------------------------------------------------------------------------
# Persistently sets PHP-FPM pm.max_children on unRAID.
# Config file path and max children value are set in Master.conf.
# Supports --dry-run to preview what would be changed without making changes.
# -----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_GEAR Setup ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_GEAR Setup ━━━"
# ROOT CHECK
if [[ "$EUID" -ne 0 ]]; then
error "Must be run as root"
exit 1
fi
success "Running as root"
# VALIDATION
validate_int PHP_MAX_CHILDREN "$PHP_MAX_CHILDREN"
require_var PHP_CONF
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Status ━━━
# -----------------------------------------------------------------------------------------------
if [[ "$SHOW_STATUS" == true ]]; then
echo ""
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
echo "$ICON_GEAR Config File: $PHP_CONF"
echo "$ICON_PHP Max Children: $PHP_MAX_CHILDREN"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo "━━━━━━━━━━━━━━━━━━━━━━━"
exit 0
fi
# -----------------------------------------------------------------------------------------------
# FUNCTIONS
# -----------------------------------------------------------------------------------------------
# Applies pm.max_children to the PHP-FPM config file and restarts the service.
# Verifies the value was applied correctly after restart.
# Skips all changes if dry run is active.
apply_php_max_children() {
local target="pm.max_children = $PHP_MAX_CHILDREN"
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would set pm.max_children = $PHP_MAX_CHILDREN in $PHP_CONF"
warn "DRY RUN — would restart PHP-FPM service"
return
fi
# Verify config file exists before attempting changes
if [[ ! -f "$PHP_CONF" ]]; then
error "PHP config file not found: $PHP_CONF"
exit 1
fi
info "Applying pm.max_children = $PHP_MAX_CHILDREN..."
if sed -i "s/^pm\.max_children.*/$target/" "$PHP_CONF"; then
success "Config updated"
else
error "Failed to update PHP config: $PHP_CONF"
exit 1
fi
info "Restarting PHP-FPM..."
if /etc/rc.d/rc.php-fpm restart; then
success "PHP-FPM restarted"
else
error "PHP-FPM restart failed"
exit 1
fi
# Verify the value was applied correctly
local current
current=$(grep -E "^pm\.max_children" "$PHP_CONF" || true)
if [[ -n "$current" ]]; then
success "Verified: $current"
logger "Userscript: PHP-FPM updated → $current"
else
warn "Could not verify configuration value — check $PHP_CONF manually"
fi
}
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_PHP PHP-FPM Config ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_PHP PHP-FPM Config ━━━"
echo "$ICON_GEAR Config File: $PHP_CONF"
echo "$ICON_PHP Max Children: $PHP_MAX_CHILDREN"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo ""
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
START=$(date +%s)
apply_php_max_children
END=$(date +%s)
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Summary ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━━━ $ICON_SUMMARY PHP-FPM SUMMARY ━━━━━"
echo "$ICON_GEAR Config File: $PHP_CONF"
echo "$ICON_PHP Max Children: $PHP_MAX_CHILDREN"
echo "$ICON_TIME Duration: $(format_duration $((END - START)))"
if [[ "$DRY_RUN" == true ]]; then
echo "$ICON_WARN Status: DRY RUN — no changes made"
else
echo "$ICON_DONE Status: $ICON_SUCCESS DONE"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+192
View File
@@ -0,0 +1,192 @@
#!/bin/bash
# -----------------------------------------------------------------------------------------------
# --------------------------------- Rsync Stop Script ------------------------------------------
# -----------------------------------------------------------------------------------------------
# Stops all running rsync processes on both the local and remote server.
# Used during array stop or manually when rsync needs to be forcefully terminated.
# If rsync processes were killed locally, checks all profile containers and restarts any
# that were left stopped by the interrupted rsync run.
# Remote is killed but left in whatever container state it is in — secondary is self-healing.
# Supports --dry-run to preview what would be killed without making changes.
# -----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_GEAR Setup ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_GEAR Setup ━━━"
detect_hosts
resolve_remote_ip
# Check connectivity but do not exit on failure — remote may already be going down
REMOTE_REACHABLE=true
if ! ping -c1 -W3 "$REMOTE_SERVER" &>/dev/null; then
warn "$ICON_PING Remote $REMOTE_SERVER_NAME is unreachable — will skip remote kill"
REMOTE_REACHABLE=false
else
info "$ICON_PING $REMOTE_SERVER_NAME is reachable"
fi
[[ "$SHOW_STATUS" == true ]] && show_status && exit 0
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_STOP Local Rsync ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_STOP Local Rsync ━━━"
LOCAL_KILLED=false
LOCAL_PIDS=$(pgrep -x rsync || true)
if [[ -z "$LOCAL_PIDS" ]]; then
info "No rsync processes running locally — nothing to kill"
else
info "Found rsync processes locally: $(echo "$LOCAL_PIDS" | tr '\n' ' ')"
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would kill local rsync processes"
else
if pkill -x rsync; then
success "Local rsync processes killed"
LOCAL_KILLED=true
else
warn "pkill returned non-zero — processes may have already exited"
fi
fi
fi
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_STOP Remote Rsync ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_STOP Remote Rsync ($REMOTE_SERVER_NAME) ━━━"
REMOTE_KILLED=false
if [[ "$REMOTE_REACHABLE" == false ]]; then
warn "Skipping remote kill — $REMOTE_SERVER_NAME unreachable"
else
REMOTE_PIDS=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
"pgrep -x rsync || true" 2>/dev/null || true)
if [[ -z "$REMOTE_PIDS" ]]; then
info "No rsync processes running on $REMOTE_SERVER_NAME — nothing to kill"
else
info "Found rsync processes on $REMOTE_SERVER_NAME: $(echo "$REMOTE_PIDS" | tr '\n' ' ')"
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would kill remote rsync processes"
else
if ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" "pkill -x rsync || true" 2>/dev/null; then
success "Remote rsync processes killed"
REMOTE_KILLED=true
else
warn "Remote pkill returned non-zero — processes may have already exited"
fi
fi
fi
fi
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_START $ICON_CONTAINERS Local Container Recovery ━━━
# Only runs if rsync was actually killed locally — containers may have been left stopped
# by the interrupted rsync run. Checks all containers across all profiles and restarts
# any that are currently stopped. Remote containers are left in their current state.
# -----------------------------------------------------------------------------------------------
CONTAINERS_RESTARTED=()
if [[ "$LOCAL_KILLED" == true ]]; then
echo ""
echo "━━━ $ICON_START $ICON_CONTAINERS Local Container Recovery ━━━"
info "Rsync was killed locally — checking all profile containers..."
# Build deduplicated list of all containers across all profiles
declare -A SEEN
ALL_CONTAINERS=()
for profile_containers in "${PROFILE_CRITICAL_CONTAINER_NAMES[@]}"; do
read -r -a container_list <<< "$profile_containers"
for c in "${container_list[@]}"; do
[[ -z "$c" ]] && continue
if [[ -z "${SEEN[$c]:-}" ]]; then
SEEN[$c]=1
ALL_CONTAINERS+=("$c")
fi
done
done
if [[ ${#ALL_CONTAINERS[@]} -eq 0 ]]; then
info "No containers defined across any profile — skipping recovery"
else
for c in "${ALL_CONTAINERS[@]}"; do
info "Checking $c..."
STATUS=$(docker inspect -f '{{.State.Running}}' "$c" 2>/dev/null || echo "unknown")
if [[ "$STATUS" == "true" ]]; then
echo "$ICON_RUNNING $c is running — no action needed"
elif [[ "$STATUS" == "false" ]]; then
echo "$ICON_NOT_RUNNING $c is stopped — restarting..."
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would restart $c"
else
if docker start "$c" >/dev/null 2>&1; then
echo "$ICON_STARTED $c restarted"
CONTAINERS_RESTARTED+=("$c")
else
error "Failed to restart $c"
fi
fi
else
warn "$c state unknown — may not exist on this machine, skipping"
fi
done
fi
elif [[ "$DRY_RUN" == false ]]; then
info "No local rsync was killed — skipping container recovery"
fi
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Summary ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━━━ $ICON_SUMMARY RSYNC STOP SUMMARY ━━━━━"
echo "$ICON_HOST Local ($LOCAL_SERVER_NAME):"
if [[ "$LOCAL_KILLED" == true ]]; then
echo " $ICON_STOPPED Rsync killed"
elif [[ "$DRY_RUN" == true ]]; then
echo " $ICON_WARN Dry run — no changes made"
else
echo " $ICON_SUCCESS No rsync running"
fi
echo "$ICON_NET Remote ($REMOTE_SERVER_NAME):"
if [[ "$REMOTE_REACHABLE" == false ]]; then
echo " $ICON_WARN Unreachable — state unknown"
elif [[ "$REMOTE_KILLED" == true ]]; then
echo " $ICON_STOPPED Rsync killed"
else
echo " $ICON_SUCCESS No rsync running"
fi
if [[ ${#CONTAINERS_RESTARTED[@]} -gt 0 ]]; then
echo "$ICON_CONTAINERS Containers restarted locally:"
for c in "${CONTAINERS_RESTARTED[@]}"; do
echo " $ICON_STARTED $c"
done
elif [[ "$LOCAL_KILLED" == true ]]; then
echo "$ICON_CONTAINERS No containers needed restarting"
fi
echo "$ICON_TIME Dry Run: $DRY_RUN"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+159
View File
@@ -0,0 +1,159 @@
#!/bin/bash
# -----------------------------------------------------------------------------------------------
# --------------------------------- Server Reboot Script ---------------------------------------
# -----------------------------------------------------------------------------------------------
# Gracefully reboots the unRAID server with a configurable user warning delay.
# Stops Docker and VM Manager cleanly before issuing reboot.
# Reboot delay is configured in Master.conf under REBOOT_SLEEP.
# Supports --dry-run to walk through the sequence without actually rebooting.
# -----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_GEAR Setup ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_GEAR Setup ━━━"
# ROOT CHECK
if [[ "$EUID" -ne 0 ]]; then
error "Must be run as root"
exit 1
fi
success "Running as root"
# VALIDATION
validate_int REBOOT_SLEEP "$REBOOT_SLEEP"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Status ━━━
# -----------------------------------------------------------------------------------------------
if [[ "$SHOW_STATUS" == true ]]; then
echo ""
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
echo "$ICON_REBOOT Delay: ${REBOOT_SLEEP}s"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo "━━━━━━━━━━━━━━━━━━━━━━━"
exit 0
fi
# -----------------------------------------------------------------------------------------------
# FUNCTIONS
# -----------------------------------------------------------------------------------------------
# Broadcasts a wall message warning all logged in users of the upcoming reboot.
notify_users() {
warn "Notifying users — reboot in ${REBOOT_SLEEP}s"
wall "$ICON_WARN unRAID server will reboot in ${REBOOT_SLEEP} second(s). Save your work."
}
# Stops the Docker service cleanly.
# Warns but continues if Docker is already stopped or fails — shutdown must proceed.
stop_docker() {
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would stop Docker service"
return
fi
info "Stopping Docker service..."
if /etc/rc.d/rc.docker stop; then
success "Docker stopped"
else
warn "Docker stop failed or already stopped — continuing"
fi
}
# Stops the VM Manager (libvirt) cleanly.
# Warns but continues if libvirt is already stopped or fails — shutdown must proceed.
stop_vm_manager() {
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would stop VM Manager (libvirt)"
return
fi
info "Stopping VM Manager..."
if /etc/rc.d/rc.libvirt stop; then
success "VM Manager stopped"
else
warn "VM Manager stop failed or already stopped — continuing"
fi
}
# Flushes filesystem buffers to disk before reboot.
sync_disks() {
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would sync filesystem buffers"
return
fi
info "Syncing disks..."
if sync; then
success "Disk sync complete"
else
warn "Sync returned an error — continuing"
fi
}
# Issues the system reboot command.
# System will not return from this call unless dry-run is active.
reboot_system() {
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would reboot system now"
return
fi
echo ""
echo "$ICON_REBOOT Rebooting system NOW..."
/sbin/reboot
}
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_REBOOT Reboot Sequence ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_REBOOT Reboot Sequence ━━━"
echo "$ICON_REBOOT Delay: ${REBOOT_SLEEP}s"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo ""
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
START=$(date +%s)
if [[ "$REBOOT_SLEEP" -gt 0 ]]; then
notify_users
info "Waiting ${REBOOT_SLEEP}s before shutdown sequence..."
sleep "$REBOOT_SLEEP"
fi
stop_docker
stop_vm_manager
sync_disks
reboot_system
# NOTE: system will not reach here unless --dry-run is active
END=$(date +%s)
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Summary ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━━━ $ICON_SUMMARY REBOOT SUMMARY ━━━━━"
echo "$ICON_REBOOT Delay: ${REBOOT_SLEEP}s"
echo "$ICON_TIME Duration: $(format_duration $((END - START)))"
if [[ "$DRY_RUN" == true ]]; then
echo "$ICON_WARN Status: DRY RUN — no reboot executed"
else
echo "$ICON_REBOOT Status: $ICON_WARN SYSTEM SHOULD BE REBOOTING"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+125
View File
@@ -0,0 +1,125 @@
#!/bin/bash
# -----------------------------------------------------------------------------------------------
# --------------------------------- User Script Stop -------------------------------------------
# -----------------------------------------------------------------------------------------------
# Stops all running User Script processes spawned by the unRAID User Scripts plugin.
# Identifies processes by their /tmp/user.scripts path signature.
# Supports --dry-run to preview what would be killed without making changes.
# -----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_GEAR Setup ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_GEAR Setup ━━━"
# ROOT CHECK
if [[ "$EUID" -ne 0 ]]; then
error "Must be run as root"
exit 1
fi
success "Running as root"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Status ━━━
# -----------------------------------------------------------------------------------------------
if [[ "$SHOW_STATUS" == true ]]; then
echo ""
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
echo "$ICON_PLUGIN Target: /tmp/user.scripts processes"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo "━━━━━━━━━━━━━━━━━━━━━━━"
exit 0
fi
# -----------------------------------------------------------------------------------------------
# FUNCTIONS
# -----------------------------------------------------------------------------------------------
# Returns PIDs of all processes running under /tmp/user.scripts
# These are processes spawned by the unRAID User Scripts plugin.
get_user_script_pids() {
/usr/bin/ps -eo pid,cmd | grep "/tmp/user.scripts" | grep -v grep | awk '{print $1}'
}
# Kills all running User Script processes one by one.
# Reports each PID killed or skipped in dry run mode.
# Checks remaining processes after kill to confirm cleanup.
stop_user_scripts() {
local pids
pids=$(get_user_script_pids)
if [[ -z "$pids" ]]; then
info "$ICON_PLUGIN No running User Script processes found — nothing to do"
return
fi
local count=0
for pid in $pids; do
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would kill User Script PID $pid"
else
info "Killing User Script PID $pid..."
if kill "$pid" 2>/dev/null; then
success "Killed PID $pid"
else
warn "Could not kill PID $pid — may have already exited"
fi
fi
count=$((count + 1))
done
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would have targeted $count process(es)"
else
info "$count process(es) targeted"
fi
}
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_PLUGIN User Script Stop ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_PLUGIN User Script Stop ━━━"
echo "$ICON_PLUGIN Target: User Scripts Plugin processes"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo ""
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
START=$(date +%s)
stop_user_scripts
END=$(date +%s)
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Summary ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━━━ $ICON_SUMMARY USER SCRIPTS STOP SUMMARY ━━━━━"
echo "$ICON_TIME Duration: $(format_duration $((END - START)))"
if [[ "$DRY_RUN" == true ]]; then
echo "$ICON_WARN Status: DRY RUN — no processes killed"
else
REMAINING=$(get_user_script_pids)
if [[ -z "$REMAINING" ]]; then
echo "$ICON_DONE Status: $ICON_SUCCESS ALL PROCESSES STOPPED"
else
echo "$ICON_WARN Status: $ICON_WARN SOME PROCESSES MAY STILL BE RUNNING"
fi
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+103
View File
@@ -0,0 +1,103 @@
#!/bin/bash
# -----------------------------------------------------------------------------------------------
# --------------------------------- ZFS Memory Snapshot ----------------------------------------
# -----------------------------------------------------------------------------------------------
# Captures a point-in-time snapshot of ZFS ARC statistics and system memory usage.
# Read-only diagnostic tool — no changes are made to the system regardless of flags.
# Dry run mode still collects and displays data since no modifications occur.
# -----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_GEAR Setup ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_GEAR Setup ━━━"
info "$ICON_ZFS ZFS ARC + Memory Snapshot"
info "$ICON_TIME $(date)"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Status ━━━
# -----------------------------------------------------------------------------------------------
if [[ "$SHOW_STATUS" == true ]]; then
echo ""
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
echo "$ICON_ZFS Mode: Read-only diagnostics"
echo "$ICON_MEM Source: ZFS ARC + system memory"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo "━━━━━━━━━━━━━━━━━━━━━━━"
exit 0
fi
# -----------------------------------------------------------------------------------------------
# FUNCTIONS
# -----------------------------------------------------------------------------------------------
# Reads ZFS ARC statistics from the kernel stats interface.
# Filters for the most useful ARC metrics — size, hits, misses and metadata.
# Skips gracefully if ZFS is not available on this system.
show_zfs_arc() {
echo ""
echo "━━━ $ICON_ZFS ZFS ARC Stats ━━━"
if [[ ! -r /proc/spl/kstat/zfs/arcstats ]]; then
warn "ZFS arcstats not available on this system — is ZFS loaded?"
return
fi
grep -iE '^(c|size|hits|misses|arc_meta_used|demand_metadata_misses|mru_ghost_metadata|mfu_ghost_metadata)' \
/proc/spl/kstat/zfs/arcstats 2>/dev/null || warn "Unable to read ARC stats"
}
# Reads current system memory and swap usage using free.
# Skips gracefully if free is not available.
show_memory_status() {
echo ""
echo "━━━ $ICON_MEM Memory Status ━━━"
if ! command -v free >/dev/null 2>&1; then
warn "free command not available on this system"
return
fi
free -h | awk '
NR==1 { print $0 }
/Mem:/ { print $0 }
/Swap:/ { print $0 }'
}
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_ZFS Snapshot ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_ZFS Snapshot ━━━"
echo "$ICON_ZFS Source: ZFS ARC + system memory"
echo "$ICON_GEAR Dry Run: $DRY_RUN"
echo ""
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — this script is read-only, data will still be collected"
START=$(date +%s)
show_zfs_arc
show_memory_status
END=$(date +%s)
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Summary ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━━━ $ICON_SUMMARY SNAPSHOT SUMMARY ━━━━━"
echo "$ICON_ZFS Source: ZFS ARC + system memory"
echo "$ICON_GEAR Mode: READ-ONLY — no changes made"
echo "$ICON_TIME Duration: $(format_duration $((END - START)))"
echo "$ICON_DONE Status: $ICON_SUCCESS DONE"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+173
View File
@@ -0,0 +1,173 @@
#!/bin/bash
# ==============================================================================================
# ================================= User Script Template =======================================
# ==============================================================================================
#
# This file is the master template for all scripts run via the unRAID User Scripts plugin.
# Copy and paste the contents of this file into a new User Script entry in the plugin,
# then uncomment the script you want to run and set your schedule.
#
# All scripts in this ecosystem live at:
# /mnt/user/appdata/unraid_scripts/
#
# Configuration for all scripts is managed in one place:
# /mnt/user/appdata/unraid_scripts/Master.conf
#
# Shared runtime functions used by all scripts:
# /mnt/user/appdata/unraid_scripts/common.sh
#
# For full setup instructions see:
# /mnt/user/appdata/unraid_scripts/README.md
#
# ==============================================================================================
# Changelog:
# v1.0 — Initial template
# v1.1 — MAX_RSYNC_PROCS removed — bandwidth limiting handles concurrency
# Typo fixes Master.cong → Master.conf
# Added --status flag to arguments section
# Added full directory tree
# All script calls pre-written and grouped by type
# Changelog added
# ==============================================================================================
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ━━━ 📂 Repository Structure ━━━
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
#
# /mnt/user/appdata/unraid_scripts/
# ├── Master.conf # All user configuration — edit this file only
# ├── common.sh # Shared library — functions used by all scripts
# ├── README.md # Project overview and quick start
# ├── User_Script_Template.sh # This file — copy into User Scripts plugin
# │
# ├── Orchestrators/
# │ └── daily_sync.sh # Runs all daily media share syncs sequentially
# │ # add shares to master.conf, for seqential syncs
# ├── Rsync/
# │ ├── rsync.sh # Core rsync script — called per share or profile
# │ └── README_Rsync_Setup.md # Rsync-specific setup guide
# │
# ├── Tools/
# │ └── recreate_shares.sh # Recreates share dirs from .cfg files after incident
# │
# └── unRAID_Essentials/
# ├── clear_logs.sh # Clears unRAID log files
# ├── docker_syslog_filter.sh # Filters docker veth noise from syslog
# ├── mover_stop.sh # Safely stops the unRAID mover
# ├── php_fpm_max_children.sh # Sets php-fpm max children value
# ├── rsync_stop.sh # Cleanly stops all running rsync processes
# ├── server_reboot.sh # Graceful server reboot with user warning
# ├── user_script_stop.sh # Stops a running User Scripts job
# └── zfs_memory_snapshot.sh # Creates a ZFS memory snapshot
#
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ━━━ 🚀 Script Commands — Uncomment the one you want to run ━━━
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
#
# ━━━ Orchestrators (README) ━━━
# The daily orchestrator runs all bulk media shares sequentially in a single scheduled job.
# Instead of creating a User Script entry per share, add the share path to DAILY_SYNC_SHARES
# in Master.conf and the orchestrator handles it automatically at 1am.
#
# Media shares are nothing special — they carry no profile and fall through to global defaults
# in Master.conf. Sequential execution means one share finishes before the next starts,
# no concurrency needed, bandwidth limiting keeps things sane if appdata jobs overlap.
#
# This is where 80% of your rsync shares should live. Only create individual scheduled
# entries for shares that need their own timing — like appdata profiles above.
#
# ━━━ Orchestrators ━━━
#/mnt/user/appdata/unraid_scripts/Orchestrators/daily_sync.sh
#
# ━━━ Rsync — Appdata Profiles (scheduled individually) ━━━
#/mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-Failover/Arrs_Stack
#/mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-Failover/Critical-Data
#/mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-Failover/Important-Data
#/mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-Failover/Emby
#/mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-Failover/Gmer4Lfe
#
# ━━━ Rsync — Individual Media Share (ad hoc use) ━━━
#/mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/Movies
#/mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/Tv_Shows
#
# ━━━ Tools ━━━
#/mnt/user/appdata/unraid_scripts/Tools/recreate_shares.sh
#
# ━━━ unRAID Essentials ━━━
#/mnt/user/appdata/unraid_scripts/unRAID_Essentials/clear_logs.sh
#/mnt/user/appdata/unraid_scripts/unRAID_Essentials/docker_syslog_filter.sh
#/mnt/user/appdata/unraid_scripts/unRAID_Essentials/mover_stop.sh
#/mnt/user/appdata/unraid_scripts/unRAID_Essentials/php_fpm_max_children.sh
#/mnt/user/appdata/unraid_scripts/unRAID_Essentials/rsync_stop.sh
#/mnt/user/appdata/unraid_scripts/unRAID_Essentials/server_reboot.sh
#/mnt/user/appdata/unraid_scripts/unRAID_Essentials/user_script_stop.sh
#/mnt/user/appdata/unraid_scripts/unRAID_Essentials/zfs_memory_snapshot.sh
#
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ━━━ ⚙️ Arguments — Add after the script path ━━━
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
#
# ━━━ Rsync / Orchestrators ━━━
#
# --dry-run Preview what would be transferred — no changes made
# --log Enable verbose logging output
# --no-log Disable logging (overrides Master.conf setting)
# --status Print resolved profile and configuration then exit
# --help Show usage information
#
# KEY=VALUE Override any Master.conf variable for this run only
# Better to add a profile in Master.conf for permanent changes
#
# ━━━ unRAID Essentials ━━━
#
# Arguments vary per script — see comments inside each script for details
#
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ━━━ 📋 Profile System ━━━
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
#
# Profiles are defined in Master.conf and matched automatically by the
# basename of the directory path passed to rsync.sh (lowercased).
#
# Example:
# /mnt/user/appdata-Failover/Arrs_Stack → matches profile key [arrs_stack]
# /mnt/user/Movies → no match, uses global defaults
#
# If a profile match is found — profile settings are used for that run
# If no profile match is found — global defaults from Master.conf are used
#
# Profile settings control:
# - rsync options PROFILE_RSYNC_OPTS
# - bandwidth limit PROFILE_BW_LIMIT
# - retry count PROFILE_RETRY_COUNT
# - sleep between retry PROFILE_SLEEP
# - containers to stop PROFILE_CRITICAL_CONTAINER_NAMES
# - delayed containers PROFILE_DELAYED_CONTAINERS
# - container delay PROFILE_CONTAINER_DELAY
# - excluded dirs PROFILE_EXCLUDE_DIRS
#
# To add a new profile — add a key to each array in Master.conf
# For permanent changes — always use Master.conf
# For one-off overrides — use KEY=VALUE arguments (see above)
#
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ━━━ 💡 Examples ━━━
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
#
# Run arrs_stack profile sync:
# /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-Failover/Arrs_Stack
#
# Dry run with logging enabled:
# /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-Failover/Arrs_Stack --dry-run --log
#
# Check what profile and settings resolved before running:
# /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/appdata-Failover/Arrs_Stack --status
#
# Override bandwidth limit for this run only:
# /mnt/user/appdata/unraid_scripts/Rsync/rsync.sh /mnt/user/Movies BW_LIMIT=5000
#
# Run daily orchestrator:
# /mnt/user/appdata/unraid_scripts/Orchestrators/daily_sync.sh
#
# ==============================================================================================