sync-shares 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. #!/bin/bash
  2. ################################################################################
  3. # Sync shares from one location to another.
  4. #
  5. # For backing up shares (usual action), use `sync-shares backup {OPTIONS}`
  6. # To restore from a backup, use `sync-shares restore {OPTIONS}`
  7. # To see options, use `sync-shares --help`
  8. ################################################################################
  9. set -e
  10. # Uncomment to show all commands as they run (used to debug the script).
  11. # set -x
  12. # The root directory where everything is stored in.
  13. # The specific file is determined after a mode is specified.
  14. LOG_ROOT="~jason/logs"
  15. # The external drive
  16. EXTERN_ROOT='/mnt/external'
  17. # The root of BTRFS shares to sync to/from
  18. SHARES_ROOT='/mnt2'
  19. # The external drive to mount.
  20. EXTERN_DEV=`ls /dev/sd* | sort -r | head -n1`
  21. CHECKSUM=""
  22. DRY_RUN=""
  23. DELETE=""
  24. VERBOSE=""
  25. UNMOUNT=""
  26. LARGE=""
  27. FULL_ARGS="$@"
  28. USAGE="sudo $0 (backup|restore) [--help] [<options>]"
  29. DESC=`cat << EOT
  30. Modes:
  31. backup: Backup shares from $SHARES_ROOT to the external drive at $EXTERN_ROOT
  32. restore: Restore files in directories in $EXTERN_ROOT to $SHARES_ROOT
  33. Options:
  34. --help: Show this message.
  35. --checksum: Compare duplicate files using checksums, rather than date/size.
  36. --dry-run: Show what would be transferred, without actually copying anything.
  37. --delete: Remove files from the destination that no longer exist in the source.
  38. eg: When running 'backup' any files deleted from a share since the
  39. last run will be deleted from the backup.
  40. --mount: Mount ${EXTERN_DEV?} to ${EXTERN_ROOT?}.
  41. --unmount: Unmount $EXTERN_ROOT when the sync has completed.
  42. --large: Include large shares that would otherwise be excluded.
  43. --verbose: Run rsync in verbose mode.
  44. EOT`
  45. function showUsage() {
  46. echo "Usage:" >&2
  47. echo "$ $USAGE" >&2
  48. echo "$DESC" >&2
  49. }
  50. function checkModeSet() {
  51. if [ "$mode" ]; then
  52. echo "Only one mode can be set." >&2
  53. exit -1
  54. fi
  55. }
  56. while :; do
  57. case $1 in
  58. -h|--help)
  59. showUsage
  60. exit -2
  61. ;;
  62. backup)
  63. checkModeSet
  64. mode=backup
  65. SRC_ROOT=$SHARES_ROOT
  66. DEST_ROOT=$EXTERN_ROOT
  67. ;;
  68. restore)
  69. checkModeSet
  70. mode=restore
  71. SRC_ROOT=$EXTERN_ROOT
  72. DEST_ROOT=$SHARES_ROOT
  73. ;;
  74. --checksum)
  75. echo "Using checksum comparison."
  76. CHECKSUM="--checksum"
  77. ;;
  78. --dry-run)
  79. echo "Running in dry-run mode."
  80. DRY_RUN="--dry-run"
  81. VERBOSE="--verbose"
  82. ;;
  83. --delete)
  84. echo "Deleting extraneous files."
  85. DELETE="--delete-after"
  86. ;;
  87. --mount)
  88. echo "Will mount ${EXTERN_DEV?}, and unmount when done."
  89. MOUNT="mount"
  90. UNMOUNT="unmount"
  91. ;;
  92. --unmount)
  93. echo "Will unmount when done."
  94. UNMOUNT="unmount"
  95. ;;
  96. --large)
  97. echo "Will include large shares."
  98. LARGE="large"
  99. ;;
  100. -v|--verbose)
  101. VERBOSE="--verbose"
  102. ;;
  103. "") break ;;
  104. *)
  105. printf "ERROR: Unknown option: %s\n" "$1" >&2
  106. showUsage
  107. exit -1
  108. ;;
  109. esac
  110. shift
  111. done
  112. echo
  113. # Check that the mode has been set
  114. if [ "$mode" = "" ]; then
  115. showUsage
  116. exit -2
  117. fi
  118. echo "Checking that script is being run as root..."
  119. CURR_USER=`whoami`
  120. if [ "$CURR_USER" != "root" ]; then
  121. echo
  122. >&2 echo "ERROR: Must be run as root"
  123. exit -3
  124. fi
  125. # Check if the drive is mounted.
  126. # Grep returns 1 when the string isn't found, which is an error, and `set -e`
  127. # stops the script. Extra bit at the end makes it return an empty string
  128. # when it's not found.
  129. MOUNTED=`mount | grep $EXTERN_ROOT || [[ $? == 1 ]]`
  130. if [ "$MOUNTED" != "" ]; then
  131. echo "${EXTERN_ROOT?} is already mounted."
  132. elif [ "$MOUNT" = "mount" ]; then
  133. echo "Mounting ${EXTERN_DEV?} to ${EXTERN_ROOT?}"
  134. mount ${EXTERN_DEV?} ${EXTERN_ROOT?}
  135. fi
  136. # Do a second check regardless of --mount to ensure everything is OK.
  137. echo "Checking that drive is mounted at ${EXTERN_ROOT?} ..."
  138. MOUNTED=`mount | grep $EXTERN_ROOT || [[ $? == 1 ]]`
  139. if [ "$MOUNTED" = "" ]; then
  140. >&2 echo "$EXTERN_ROOT is not mounted!"
  141. exit -4
  142. fi
  143. LOG="${LOG_ROOT?}/$mode-shares-$(date '+%Y-%m-%d_%H-%M').log"
  144. echo "Everything looks good. Will log to ${LOG?}"
  145. function log() {
  146. date_str=`date '+%Y-%m-%d %H:%M:%S'`
  147. echo "[$date_str]: $1" 2>&1 | tee -a $LOG
  148. }
  149. function logCmd() {
  150. cmd="$@"
  151. log "Running '${cmd}'"
  152. (time $cmd) 2>&1 | tee -a $LOG
  153. }
  154. function pause() {
  155. read -s -n 1 -p "Press any key to continue, or ctrl+c to cancel."
  156. }
  157. function syncShare() {
  158. localShare="$1"
  159. if [ "$2" = "" ]; then
  160. remoteShare="$1"
  161. else
  162. remoteShare="$2"
  163. fi
  164. if [ "$mode" = "restore" ]; then
  165. srcShare="$remoteShare"
  166. destShare="$localShare"
  167. else
  168. srcShare="$localShare"
  169. destShare="$remoteShare"
  170. fi
  171. # use a trailing slash to sync the contents of the directory
  172. src="$SRC_ROOT/$srcShare/"
  173. dest="$DEST_ROOT/$destShare/"
  174. echo "" | tee -a $LOG
  175. log '============================'
  176. log "Syncing $src to $dest"
  177. log "Checking if $dest exists"
  178. if [[ ! -d "$dest" ]]; then
  179. if [ "$mode" = "backup" ]; then
  180. echo "$dest does not exist. Creating it."
  181. mkdir -p $dest
  182. else
  183. echo "$dest does not exist. Exiting."
  184. exit 1
  185. fi
  186. fi
  187. if [ "$1" = "bittorrent" ]; then
  188. DELETE="--delete-before"
  189. fi
  190. # Set noglob to prevent * from being expanded before logCmd is called
  191. set -o noglob
  192. logCmd rsync --archive --hard-links $DRY_RUN $VERBOSE \
  193. --progress --partial \
  194. --human-readable \
  195. --exclude ".[!.]*-daily_20*/" \
  196. --exclude ".[!.]*-weekly_20*/" \
  197. --exclude ".daily_20*/" \
  198. --exclude ".weekly_20*/" \
  199. --exclude "transcoding-temp/" \
  200. --exclude ".mount" \
  201. $CHECKSUM \
  202. $DELETE \
  203. $src \
  204. $dest
  205. set +o noglob
  206. }
  207. function syncShares() {
  208. pause
  209. log "Starting sync"
  210. syncShare bitwarden
  211. syncShare jason Jason
  212. syncShare jenn Jenn
  213. syncShare nginx-config
  214. syncShare git-config
  215. syncShare git-storage
  216. syncShare party-rescue
  217. syncShare shared
  218. #syncShare video-recordings
  219. syncShare music
  220. #syncShare nginx-data
  221. syncShare emby-config
  222. #syncShare odoo-data
  223. if [ "$LARGE" ]; then
  224. syncShare bittorrent
  225. fi
  226. }
  227. function unmount() {
  228. if [ "$UNMOUNT" ]; then
  229. log "Unmounting drive"
  230. logCmd sudo umount $EXTERN_ROOT
  231. fi
  232. }
  233. log "Arguments: $FULL_ARGS"
  234. logCmd syncShares
  235. unmount
  236. log "Done"