diff --git a/Rsync/rsync.sh b/Rsync/rsync.sh index 7cd9e30..1d447e9 100755 --- a/Rsync/rsync.sh +++ b/Rsync/rsync.sh @@ -371,10 +371,10 @@ RSYNC_OPTS+=(--stats) # Runs before the push so anything the remote has that we don't is preserved locally. # After this pass, local is the superset — the delete push in pass 2 is then safe. if [[ "$MERGE_RUN" == true ]]; then - echo "$ICON_SYNC Merge pass 1 — pulling ${REMOTE_SERVER_NAME}-unique content to local..." + echo "$ICON_SYNC Merge pass 1 — pulling ${REMOTE_SERVER_NAME}-unique content to local (capped at ${RSYNC_MAX_RUNTIME_HOURS}h)..." _pull_opts=(-av --ignore-existing --stats) [[ "$DRY_RUN" == true ]] && _pull_opts+=(--dry-run) - _pull_output=$(rsync "${_pull_opts[@]}" \ + _pull_output=$(timeout "${RSYNC_MAX_RUNTIME_SECONDS}s" rsync "${_pull_opts[@]}" \ -e "ssh -i $SSH_KEY -T -o Compression=no -o IPQoS=throughput" \ "root@${REMOTE_SERVER}:${DIRECTORY}/" "${DIRECTORY}/" 2>&1) _pull_exit=$? @@ -382,6 +382,12 @@ if [[ "$MERGE_RUN" == true ]]; then awk '/Total transferred file size:/{gsub(/,/,"",$NF); gsub(/[^0-9]/,"",$NF); print $NF+0}') if [[ "$_pull_exit" -eq 0 ]]; then echo "$ICON_DONE Merge pass 1 complete — ${_pull_bytes:-0} bytes pulled" + elif [[ "$_pull_exit" -eq 124 ]]; then + # Same cap as the main push — never let one direction of the merge run unbounded. + # Files already completed before the timeout stay pulled; next scheduled run + # picks up whatever's still remote-unique (no --partial here, so an in-flight + # file at the moment of the kill is discarded, not corrupted). + warn "Merge pass 1 exceeded ${RSYNC_MAX_RUNTIME_HOURS}h cap — continuing with push, will resume pull next scheduled run" else warn "Merge pass 1 failed (exit $_pull_exit) — continuing with push" echo "$_pull_output" | grep -iE "error|rsync:|permission denied" | while read -r _line; do