| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- #!/bin/bash
- ################################################################################
- # Sync shares from one location to another.
- #
- # For backing up shares (usual action), use `sync-shares backup {OPTIONS}`
- # To restore from a backup, use `sync-shares restore {OPTIONS}`
- # To see options, use `sync-shares --help`
- ################################################################################
- set -e
- # Uncomment to show all commands as they run (used to debug the script).
- # set -x
- # The root directory where everything is stored in.
- # The specific file is determined after a mode is specified.
- LOG_ROOT="~jason/logs"
- # The external drive
- EXTERN_ROOT='/mnt/external'
- # The root of BTRFS shares to sync to/from
- SHARES_ROOT='/mnt2'
- # The external drive to mount.
- EXTERN_DEV=`ls /dev/sd* | sort -r | head -n1`
- CHECKSUM=""
- DRY_RUN=""
- DELETE=""
- VERBOSE=""
- UNMOUNT=""
- LARGE=""
- FULL_ARGS="$@"
- USAGE="sudo $0 (backup|restore) [--help] [<options>]"
- DESC=`cat << EOT
- Modes:
- backup: Backup shares from $SHARES_ROOT to the external drive at $EXTERN_ROOT
- restore: Restore files in directories in $EXTERN_ROOT to $SHARES_ROOT
- Options:
- --help: Show this message.
- --checksum: Compare duplicate files using checksums, rather than date/size.
- --dry-run: Show what would be transferred, without actually copying anything.
- --delete: Remove files from the destination that no longer exist in the source.
- eg: When running 'backup' any files deleted from a share since the
- last run will be deleted from the backup.
- --mount: Mount ${EXTERN_DEV?} to ${EXTERN_ROOT?}.
- --unmount: Unmount $EXTERN_ROOT when the sync has completed.
- --large: Include large shares that would otherwise be excluded.
- --verbose: Run rsync in verbose mode.
- EOT`
- function showUsage() {
- echo "Usage:" >&2
- echo "$ $USAGE" >&2
- echo "$DESC" >&2
- }
- function checkModeSet() {
- if [ "$mode" ]; then
- echo "Only one mode can be set." >&2
- exit -1
- fi
- }
- while :; do
- case $1 in
- -h|--help)
- showUsage
- exit -2
- ;;
- backup)
- checkModeSet
- mode=backup
- SRC_ROOT=$SHARES_ROOT
- DEST_ROOT=$EXTERN_ROOT
- ;;
- restore)
- checkModeSet
- mode=restore
- SRC_ROOT=$EXTERN_ROOT
- DEST_ROOT=$SHARES_ROOT
- ;;
- --checksum)
- echo "Using checksum comparison."
- CHECKSUM="--checksum"
- ;;
- --dry-run)
- echo "Running in dry-run mode."
- DRY_RUN="--dry-run"
- VERBOSE="--verbose"
- ;;
- --delete)
- echo "Deleting extraneous files."
- DELETE="--delete-after"
- ;;
- --mount)
- echo "Will mount ${EXTERN_DEV?}, and unmount when done."
- MOUNT="mount"
- UNMOUNT="unmount"
- ;;
- --unmount)
- echo "Will unmount when done."
- UNMOUNT="unmount"
- ;;
- --large)
- echo "Will include large shares."
- LARGE="large"
- ;;
- -v|--verbose)
- VERBOSE="--verbose"
- ;;
- "") break ;;
- *)
- printf "ERROR: Unknown option: %s\n" "$1" >&2
- showUsage
- exit -1
- ;;
- esac
- shift
- done
- echo
- # Check that the mode has been set
- if [ "$mode" = "" ]; then
- showUsage
- exit -2
- fi
- echo "Checking that script is being run as root..."
- CURR_USER=`whoami`
- if [ "$CURR_USER" != "root" ]; then
- echo
- >&2 echo "ERROR: Must be run as root"
- exit -3
- fi
- # Check if the drive is mounted.
- # Grep returns 1 when the string isn't found, which is an error, and `set -e`
- # stops the script. Extra bit at the end makes it return an empty string
- # when it's not found.
- MOUNTED=`mount | grep $EXTERN_ROOT || [[ $? == 1 ]]`
- if [ "$MOUNTED" != "" ]; then
- echo "${EXTERN_ROOT?} is already mounted."
- elif [ "$MOUNT" = "mount" ]; then
- echo "Mounting ${EXTERN_DEV?} to ${EXTERN_ROOT?}"
- mount ${EXTERN_DEV?} ${EXTERN_ROOT?}
- fi
- # Do a second check regardless of --mount to ensure everything is OK.
- echo "Checking that drive is mounted at ${EXTERN_ROOT?} ..."
- MOUNTED=`mount | grep $EXTERN_ROOT || [[ $? == 1 ]]`
- if [ "$MOUNTED" = "" ]; then
- >&2 echo "$EXTERN_ROOT is not mounted!"
- exit -4
- fi
- LOG="${LOG_ROOT?}/$mode-shares-$(date '+%Y-%m-%d_%H-%M').log"
- echo "Everything looks good. Will log to ${LOG?}"
- function log() {
- date_str=`date '+%Y-%m-%d %H:%M:%S'`
- echo "[$date_str]: $1" 2>&1 | tee -a $LOG
- }
- function logCmd() {
- cmd="$@"
- log "Running '${cmd}'"
- (time $cmd) 2>&1 | tee -a $LOG
- }
- function pause() {
- read -s -n 1 -p "Press any key to continue, or ctrl+c to cancel."
- }
- function syncShare() {
- localShare="$1"
- if [ "$2" = "" ]; then
- remoteShare="$1"
- else
- remoteShare="$2"
- fi
-
- if [ "$mode" = "restore" ]; then
- srcShare="$remoteShare"
- destShare="$localShare"
- else
- srcShare="$localShare"
- destShare="$remoteShare"
- fi
- # use a trailing slash to sync the contents of the directory
- src="$SRC_ROOT/$srcShare/"
- dest="$DEST_ROOT/$destShare/"
- echo "" | tee -a $LOG
- log '============================'
- log "Syncing $src to $dest"
- log "Checking if $dest exists"
- if [[ ! -d "$dest" ]]; then
- if [ "$mode" = "backup" ]; then
- echo "$dest does not exist. Creating it."
- mkdir -p $dest
- else
- echo "$dest does not exist. Exiting."
- exit 1
- fi
- fi
- if [ "$1" = "bittorrent" ]; then
- DELETE="--delete-before"
- fi
- # Set noglob to prevent * from being expanded before logCmd is called
- set -o noglob
- logCmd rsync --archive --hard-links $DRY_RUN $VERBOSE \
- --progress --partial \
- --human-readable \
- --exclude ".[!.]*-daily_20*/" \
- --exclude ".[!.]*-weekly_20*/" \
- --exclude ".daily_20*/" \
- --exclude ".weekly_20*/" \
- --exclude "transcoding-temp/" \
- --exclude ".mount" \
- $CHECKSUM \
- $DELETE \
- $src \
- $dest
- set +o noglob
- }
- function syncShares() {
- pause
- log "Starting sync"
- syncShare bitwarden
- syncShare jason Jason
- syncShare jenn Jenn
- syncShare nginx-config
- syncShare git-config
- syncShare git-storage
- syncShare party-rescue
- syncShare shared
- #syncShare video-recordings
- syncShare music
- #syncShare nginx-data
- syncShare emby-config
- #syncShare odoo-data
- if [ "$LARGE" ]; then
- syncShare bittorrent
- fi
- }
- function unmount() {
- if [ "$UNMOUNT" ]; then
- log "Unmounting drive"
- logCmd sudo umount $EXTERN_ROOT
- fi
- }
- log "Arguments: $FULL_ARGS"
- logCmd syncShares
- unmount
- log "Done"
|