diff --git a/Master.conf b/Master.conf index deaf483..f850354 100644 --- a/Master.conf +++ b/Master.conf @@ -23,8 +23,9 @@ HOST2="unRAID-Jayred365" # Master.conf declare -A SSH_KEYS -SSH_KEYS["unRAID-Gmer4Lfe|unRAID-Jayred365"]="/root/.ssh/Gmer4Lfe-rsync-key" -SSH_KEYS["unRAID-Jayred365|unRAID-Gmer4Lfe"]="/root/.ssh/Jayred365-rsync-key" +declare -A SSH_KEYS +SSH_KEYS["$HOST1|$HOST2"]="/root/.ssh/Gmer4Lfe-rsync-key" +SSH_KEYS["$HOST2|$HOST1"]="/root/.ssh/Jayred365-rsync-key" #----------------------------------------------------------------------------------------------- #----------------- User Variables -------------------------------------------------------------- #----------------------------------------------------------------------------------------------- diff --git a/Rsync/rsync.sh b/Rsync/rsync.sh index 758c468..0833b6d 100644 --- a/Rsync/rsync.sh +++ b/Rsync/rsync.sh @@ -4,11 +4,11 @@ set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../common.sh" -# Load config +# Load config and detect hosts load_master_conf detect_hosts -parse_args "$@" +# Input argument: directory DIRECTORY="$1" if [[ -z "$DIRECTORY" ]]; then echo "Usage: $0 [--dry-run|-n] [VAR=value ...]" @@ -16,11 +16,15 @@ if [[ -z "$DIRECTORY" ]]; then fi shift -# Determine profile from folder basename -PROFILE_NAME=$(basename "$DIRECTORY" | tr '[:upper:]' '[:lower:]') +# Parse CLI args +parse_args "$@" + +# Determine profile +DIR_CLEAN="${DIRECTORY%/}" # strip trailing slash +PROFILE_NAME=$(basename "$DIR_CLEAN" | tr '[:upper:]' '[:lower:]') echo "Detected profile: $PROFILE_NAME" -# Apply profile settings +# Apply profile defaults or fallback MAX_RSYNC_PROCS=${PROFILE_MAX_PROCS[$PROFILE_NAME]:-$MAX_RSYNC_PROCS} BW_LIMIT=${PROFILE_BW_LIMIT[$PROFILE_NAME]:-$BW_LIMIT} CRITICAL_CONTAINER_NAMES=(${PROFILE_CRITICAL_CONTAINER_NAMES[$PROFILE_NAME]}) @@ -30,8 +34,6 @@ RETRY_COUNT=${PROFILE_RETRY_COUNT[$PROFILE_NAME]:-$RETRY_COUNT} SLEEP=${PROFILE_SLEEP[$PROFILE_NAME]:-$SLEEP} EXCLUDE_DIRS=(${PROFILE_EXCLUDE_DIRS[$PROFILE_NAME]:-${EXCLUDE_DIRS[@]}}) - -# Validate validate_int MAX_RSYNC_PROCS "$MAX_RSYNC_PROCS" validate_int BW_LIMIT "$BW_LIMIT" validate_int CONTAINER_DELAY "$CONTAINER_DELAY" @@ -40,30 +42,76 @@ validate_int SLEEP "$SLEEP" echo "Settings: MAX_RSYNC_PROCS=$MAX_RSYNC_PROCS, BW_LIMIT=$BW_LIMIT, CONTAINERS=${CRITICAL_CONTAINER_NAMES[*]}, EXCLUDES=${EXCLUDE_DIRS[*]}" -# Main rsync function -main() { - echo "[$LOCAL_SERVER_NAME] Syncing $DIRECTORY → $REMOTE_SERVER:$DIRECTORY" +# Dependencies +for cmd in rsync ssh ping timeout docker tailscale; do + command -v "$cmd" >/dev/null 2>&1 || { echo "Error: $cmd not installed."; exit 1; } +done - # Retry server ping - for i in $(seq 1 "$RETRY_COUNT"); do - if timeout 5 ping -c1 "$REMOTE_SERVER" &>/dev/null; then break; fi - echo "Remote $REMOTE_SERVER_NAME down, retrying in $SLEEP seconds..." - sleep "$SLEEP" +trap 'echo "Script interrupted. Exiting."; exit 130' INT TERM + +# Helper functions +wait_for_rsync() { + for attempt in $(seq 1 "$RETRY_COUNT"); do + RSYNC_COUNT=$(pgrep -fc "rsync.*root@${REMOTE_SERVER}" || true) + if [ "$RSYNC_COUNT" -ge "$MAX_RSYNC_PROCS" ]; then + echo "[$LOCAL_SERVER_NAME] Waiting... ($RSYNC_COUNT active rsync processes)" + sleep "$SLEEP" + else + return 0 + fi done - timeout 5 ping -c1 "$REMOTE_SERVER" &>/dev/null || { echo "Remote $REMOTE_SERVER_NAME unreachable"; exit 1; } + echo "Too many rsync processes, exiting." + exit 1 +} - # Stop critical containers +check_server_online() { + timeout 5 ping -c 1 "$REMOTE_SERVER" &>/dev/null +} + +declare -a RUNNING_CONTAINERS=() + +stop_containers() { + echo "[$LOCAL_SERVER_NAME] Checking containers on ${REMOTE_SERVER_NAME}..." RUNNING_CONTAINERS=() for container in "${CRITICAL_CONTAINER_NAMES[@]}"; do - STATUS=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" "docker inspect -f '{{.State.Running}}' $container" 2>/dev/null || echo "false") - if [[ "$STATUS" == "true" ]]; then + STATUS=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \ + "docker inspect -f '{{.State.Running}}' $container 2>/dev/null" || echo "false") + if [ "$STATUS" == "true" ]; then echo "Stopping $container" RUNNING_CONTAINERS+=("$container") [ "$DRY_RUN" = false ] && ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" "docker stop $container" + [ "$DRY_RUN" = true ] && echo "[DRY-RUN] Would stop $container" fi done +} + +start_containers() { + echo "[$LOCAL_SERVER_NAME] Restarting containers on ${REMOTE_SERVER_NAME}..." + for container in "${RUNNING_CONTAINERS[@]}"; do + if [[ " ${DELAYED_CONTAINERS[*]} " == *" $container "* ]]; then + echo "Delaying start of $container by ${CONTAINER_DELAY}s..." + [ "$DRY_RUN" = false ] && sleep "$CONTAINER_DELAY" + fi + [ "$DRY_RUN" = true ] && echo "[DRY-RUN] Would start $container" + [ "$DRY_RUN" = false ] && ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" "docker start $container" + done +} + +# Main logic +main() { + echo "[$LOCAL_SERVER_NAME] Syncing $DIRECTORY → $REMOTE_SERVER:$DIRECTORY" + + for i in $(seq 1 "$RETRY_COUNT"); do + if check_server_online; then break; fi + echo "Remote $REMOTE_SERVER_NAME down, retrying in $SLEEP seconds..." + sleep "$SLEEP" + done + + check_server_online || { echo "Remote $REMOTE_SERVER_NAME unreachable. Exiting."; exit 1; } + + wait_for_rsync + stop_containers - # Rsync RSYNC_OPTS=(-av --info=progress2 --human-readable --bwlimit="$BW_LIMIT" --delete --inplace --no-whole-file) for pattern in "${EXCLUDE_DIRS[@]}"; do RSYNC_OPTS+=(--exclude="$pattern") @@ -77,19 +125,14 @@ main() { break else echo "Rsync attempt $attempt failed." - [ "$attempt" -lt "$RETRY_COUNT" ] && sleep "$SLEEP" || { echo "All attempts failed"; exit 1; } + [ "$attempt" -lt "$RETRY_COUNT" ] && sleep "$SLEEP" + [ "$attempt" -eq "$RETRY_COUNT" ] && { start_containers; exit 1; } fi done - # Restart containers - for container in "${RUNNING_CONTAINERS[@]}"; do - if [[ " ${DELAYED_CONTAINERS[*]} " == *" $container "* ]]; then - [ "$DRY_RUN" = false ] && sleep "$CONTAINER_DELAY" - fi - [ "$DRY_RUN" = false ] && ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" "docker start $container" - done - + start_containers echo "[$LOCAL_SERVER_NAME] Sync complete." } +# Execute main \ No newline at end of file diff --git a/common.sh b/common.sh index 784c8a3..440b962 100644 --- a/common.sh +++ b/common.sh @@ -3,10 +3,11 @@ # --------------------- Common Shared Functions for Unraid Scripts ----------------------------- #----------------------------------------------------------------------------------------------- -# Load Master.conf +# Load Master.conf relative to calling script load_master_conf() { - # Use absolute path to avoid "not found" errors - local master_conf="/mnt/user/appdata/unraid_scripts/Master.conf" + local script_dir + script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + local master_conf="$script_dir/../Master.conf" if [ -f "$master_conf" ]; then source "$master_conf" @@ -20,10 +21,12 @@ load_master_conf() { # Parse CLI args (flags + VAR=value overrides) parse_args() { DRY_RUN=${DRY_RUN:-false} + for ARG in "$@"; do if [[ "$ARG" == *=* ]]; then local VAR_NAME="${ARG%%=*}" local VAR_VALUE="${ARG#*=}" + if declare -p "$VAR_NAME" &>/dev/null; then printf -v "$VAR_NAME" '%s' "$VAR_VALUE" echo "Overriding $VAR_NAME -> $VAR_VALUE" @@ -36,7 +39,8 @@ parse_args() { --help|-h) echo "Usage: script [--dry-run|-n] [VAR=value ...]" exit 0 ;; - *) echo "Warning: Unknown argument $ARG" ;; + *) + echo "Warning: Unknown argument $ARG" ;; esac fi done @@ -52,7 +56,7 @@ validate_int() { fi } -# Require variable +# Require variable to be set require_var() { local var="$1" if [[ -z "${!var:-}" ]]; then @@ -61,10 +65,9 @@ require_var() { fi } -# Detect local hostname +# Detect local hostname and set remote detect_hosts() { LOCAL_HOSTNAME="$(hostname)" - if [[ "$LOCAL_HOSTNAME" == "$HOST1" ]]; then LOCAL_SERVER_NAME="$HOST1" REMOTE_SERVER_NAME="$HOST2" @@ -86,13 +89,8 @@ detect_hosts() { fi echo "Resolved $REMOTE_SERVER_NAME → $REMOTE_SERVER" - # SSH key mapping - if ! declare -A SSH_KEYS &>/dev/null; then - declare -A SSH_KEYS - SSH_KEYS["$HOST1|$HOST2"]="/root/.ssh/Gmer4Lfe-rsync-key" - SSH_KEYS["$HOST2|$HOST1"]="/root/.ssh/Jayred365-rsync-key" - fi - + # Require SSH_KEYS + require_var SSH_KEYS KEY_ID="$LOCAL_SERVER_NAME|$REMOTE_SERVER_NAME" if [[ -n "${SSH_KEYS[$KEY_ID]}" ]]; then SSH_KEY="${SSH_KEYS[$KEY_ID]}"