#!/bin/bash # -- net-home-backup -- aka "nfs-backer" v1.6 - July 06, 2025 -- BTA / LOSTGEEK.NET -- # CONFIG NFS_MOUNT="/mnt/nfs" NFS_SHARE="$NFS_MOUNT/Share/net-home-bk" CLIENT_HOSTNAME=$(hostname) LOCKFILE="/tmp/net-home-backup.lock" MASTER_LOG="$NFS_SHARE/backup.log" DATESTAMP=$(date '+%m/%d/%y %H:%M') FORCE=0 INTERACTIVE=0 ROOTMODE=0 # Show help show_help() { echo "Usage: $0 [options]" echo "" echo "Options:" echo " -i, --interactive Prompt before syncing if diff detected" echo " -f, --force Disable 10GB diff limit check" echo " -r, --root Backup /root/ instead of /home/ben/" echo " -h, --help Show this help message" exit 0 } # Parse options while [ $# -gt 0 ]; do case "$1" in -i|--i|--interactive) INTERACTIVE=1 ;; -f|--f|--force) FORCE=1 ;; -r|--r|--root) ROOTMODE=1 ;; -h|--h|--help) show_help ;; *) echo "Unknown option: $1" && show_help ;; esac shift done # Lockfile to prevent concurrent runs [ -f "$LOCKFILE" ] && echo "Backup already running." && exit 1 touch "$LOCKFILE" trap "rm -f $LOCKFILE" EXIT # Ensure NFS mount exists if [ ! -d "$NFS_SHARE" ]; then echo "Mounting NFS share..." sudo mkdir -p "$NFS_MOUNT" sudo chown "$USER":"$USER" "$NFS_MOUNT" sudo mount -t nfs novo.lan:/mnt/sda1 "$NFS_MOUNT" || { echo "Mount failed"; exit 1; } fi # Determine source and destination if [ "$ROOTMODE" -eq 1 ]; then SRC="/root/" DEST="$NFS_SHARE/$CLIENT_HOSTNAME/root" else SRC="/home/ben/" DEST="$NFS_SHARE/$CLIENT_HOSTNAME" fi # Create destination directory if missing mkdir -p "$DEST" # Rsync options and include/exclude list RSYNC_OPTS=(-a -x --delete --stats --info=progress2) INCLUDES=( --include='.bashrc' --include='.xinitrc' --include='.xsession' --include='.ssh/***' ) EXCLUDES=( --exclude='.*' --exclude='Downloads/' --exclude='VirtualBox VMs/' ) # Dry-run to estimate transfer size RSYNC_DRYRUN_OUTPUT=$(rsync "${RSYNC_OPTS[@]}" "${INCLUDES[@]}" "${EXCLUDES[@]}" --dry-run "$SRC" "$DEST") TOTAL_SIZE_BYTES=$(echo "$RSYNC_DRYRUN_OUTPUT" | awk '/Total transferred file size:/ {print $5}') TOTAL_SIZE_BYTES=${TOTAL_SIZE_BYTES//,/} TOTAL_SIZE_MB=$(( TOTAL_SIZE_BYTES / 1048576 )) TOTAL_SIZE_GB=$(awk "BEGIN {printf \"%.2f\", $TOTAL_SIZE_MB/1024}") # Interactive check if requested if [ "$INTERACTIVE" -eq 1 ]; then LAST_SYNC_FILE="$DEST/last_backup.txt" if [ -f "$LAST_SYNC_FILE" ]; then LAST_SYNC=$(cat "$LAST_SYNC_FILE") else LAST_SYNC="Never" fi echo "Last sync was: $LAST_SYNC" echo "Estimated new data to sync: $TOTAL_SIZE_MB MB ($TOTAL_SIZE_GB GB)" read -p "Proceed? (Y/N): " PROCEED [[ "$PROCEED" =~ ^[Yy]$ ]] || { echo "Aborting."; exit 0; } fi # Bail if diff is over 10GB and no force flag if [ "$FORCE" -eq 0 ]; then if awk "BEGIN {exit !($TOTAL_SIZE_MB > 10240)}"; then echo "Sync exceeds 10GB. Use -f or --force to override." exit 1 fi fi # Perform actual rsync echo "Starting sync..." RSYNC_OUTPUT=$(rsync "${RSYNC_OPTS[@]}" "${INCLUDES[@]}" "${EXCLUDES[@]}" "$SRC" "$DEST") echo "$RSYNC_OUTPUT" > /tmp/rsync_log.txt # Extract transfer size from rsync output ACTUAL_TRANSFER_BYTES=$(grep 'Total transferred file size:' /tmp/rsync_log.txt | awk '{print $5}') ACTUAL_TRANSFER_BYTES=${ACTUAL_TRANSFER_BYTES//,/} ACTUAL_TRANSFER_MB=$(( ACTUAL_TRANSFER_BYTES / 1048576 )) ACTUAL_TRANSFER_GB=$(awk "BEGIN {printf \"%.2f\", $ACTUAL_TRANSFER_MB/1024}") # Update last backup time echo "$DATESTAMP" > "$DEST/last_backup.txt" # Determine if this was an initial or change run if [ ! -f "$DEST/.initial_done" ]; then RUN_TYPE="Initial" touch "$DEST/.initial_done" else RUN_TYPE="Change" fi # Append to master log echo "$DATESTAMP $RUN_TYPE backup run on $CLIENT_HOSTNAME -- $ACTUAL_TRANSFER_GB GB" >> "$MASTER_LOG" # Summary output echo "" echo "Backup complete." echo "Summary for $CLIENT_HOSTNAME:" echo "----------------------------------------------" echo "New data transferred: $ACTUAL_TRANSFER_MB MB ($ACTUAL_TRANSFER_GB GB)" echo "" echo "Contents of $DEST:" ls -lh "$DEST" echo "" echo "Total size of $CLIENT_HOSTNAME directory on NFS share:" du -sh "$DEST" echo "----------------------------------------------" echo "" exit 0