command_backup.sh 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. #!/bin/bash
  2. # LinuxGSM command_backup.sh function
  3. # Author: Daniel Gibbs
  4. # Contributor: UltimateByte
  5. # Website: https://linuxgsm.com
  6. # Description: Creates a .tar.gz file in the backup directory.
  7. commandname="BACKUP"
  8. commandaction="Backing up"
  9. functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
  10. check.sh
  11. # Trap to remove lockfile on quit.
  12. fn_backup_trap(){
  13. echo -e ""
  14. echo -en "backup ${backupname}.tar.gz..."
  15. fn_print_canceled_eol_nl
  16. fn_script_log_info "Backup ${backupname}.tar.gz: CANCELED"
  17. rm -f "${backupdir:?}/${backupname}.tar.gz" | tee -a "${lgsmlog}"
  18. echo -en "backup ${backupname}.tar.gz..."
  19. fn_print_removed_eol_nl
  20. fn_script_log_info "Backup ${backupname}.tar.gz: REMOVED"
  21. # Remove lock file.
  22. rm -f "${lockdir:?}/.backup.lock"
  23. core_exit.sh
  24. }
  25. # Check if a backup is pending or has been aborted using .backup.lock.
  26. fn_backup_check_lockfile(){
  27. if [ -f "${lockdir}/.backup.lock" ]; then
  28. fn_print_info_nl "Lock file found: Backup is currently running"
  29. fn_script_log_error "Lock file found: Backup is currently running: ${lockdir}/.backup.lock"
  30. core_exit.sh
  31. fi
  32. }
  33. # Initialisation.
  34. fn_backup_init(){
  35. # Backup file name with selfname and current date.
  36. backupname="${selfname}-$(date '+%Y-%m-%d-%H%M%S')"
  37. info_distro.sh
  38. fn_print_dots "Backup starting"
  39. fn_script_log_info "Backup starting"
  40. fn_print_ok_nl "Backup starting"
  41. if [ ! -d "${backupdir}" ]||[ "${backupcount}" == "0" ]; then
  42. fn_print_info_nl "There are no previous backups"
  43. else
  44. if [ "${lastbackupdaysago}" == "0" ]; then
  45. daysago="less than 1 day ago"
  46. elif [ "${lastbackupdaysago}" == "1" ]; then
  47. daysago="1 day ago"
  48. else
  49. daysago="${lastbackupdaysago} days ago"
  50. fi
  51. echo -e "* Previous backup was created ${daysago}, total size ${lastbackupsize}"
  52. fi
  53. }
  54. # Check if server is started and whether to stop it.
  55. fn_backup_stop_server(){
  56. check_status.sh
  57. # Server is stopped.
  58. if [ "${status}" == "0" ]; then
  59. serverstopped="no"
  60. # Server is running and stoponbackup=off.
  61. elif [ "${stoponbackup}" == "off" ]; then
  62. serverstopped="no"
  63. fn_print_warn_nl "${selfname} is currently running"
  64. echo -e "* Although unlikely; creating a backup while ${selfname} is running might corrupt the backup."
  65. fn_script_log_warn "${selfname} is currently running"
  66. fn_script_log_warn "Although unlikely; creating a backup while ${selfname} is running might corrupt the backup"
  67. # Server is running and will be stopped if stoponbackup=on or unset.
  68. else
  69. fn_stop_warning
  70. serverstopped="yes"
  71. exitbypass=1
  72. command_stop.sh
  73. fi
  74. }
  75. # Create required folders.
  76. fn_backup_dir(){
  77. # Create backupdir if it doesn't exist.
  78. if [ ! -d "${backupdir}" ]; then
  79. mkdir -p "${backupdir}"
  80. fi
  81. }
  82. # Migrate Backups from old dir before refactor
  83. fn_backup_migrate_olddir(){
  84. # Check if old backup dir is there before the refactor and move the backups
  85. if [ -d "${rootdir}/backups" ]; then
  86. if [ "${rootdir}/backups" != "${backupdir}" ]; then
  87. fn_print_dots "Backup directory is being migrated"
  88. fn_script_log_info "Backup directory is being migrated"
  89. fn_script_log_info "${rootdir}/backups > ${backupdir}"
  90. mv "${rootdir}/backups/"* "${backupdir}" 2>/dev/null
  91. exitcode=$?
  92. if [ "${exitcode}" -eq 0 ]; then
  93. rmdir "${rootdir}/backups" 2>/dev/null
  94. exitcode=$?
  95. fi
  96. if [ "${exitcode}" -eq 0 ]; then
  97. fn_print_ok_nl "Backup directory is being migrated"
  98. fn_script_log_pass "Backup directory is being migrated"
  99. else
  100. fn_print_error_nl "Backup directory is being migrated"
  101. fn_script_log_error "Backup directory is being migrated"
  102. fi
  103. fi
  104. fi
  105. }
  106. fn_backup_create_lockfile(){
  107. # Create lockfile.
  108. date '+%s' > "${lockdir}/.backup.lock"
  109. fn_script_log_info "Lockfile generated"
  110. fn_script_log_info "${lockdir}/.backup.lock"
  111. # trap to remove lockfile on quit.
  112. trap fn_backup_trap INT
  113. }
  114. # Compressing files.
  115. fn_backup_compression(){
  116. # Tells how much will be compressed using rootdirduexbackup value from info_distro and prompt for continue.
  117. fn_print_info "A total of ${rootdirduexbackup} will be compressed."
  118. fn_script_log_info "A total of ${rootdirduexbackup} will be compressed: ${backupdir}/${backupname}.tar.gz"
  119. fn_print_dots "Backup (${rootdirduexbackup}) ${backupname}.tar.gz, in progress..."
  120. fn_script_log_info "backup ${rootdirduexbackup} ${backupname}.tar.gz, in progress"
  121. excludedir=$(fn_backup_relpath)
  122. # Check that excludedir is a valid path.
  123. if [ ! -d "${excludedir}" ] ; then
  124. fn_print_fail_nl "Problem identifying the previous backup directory for exclusion."
  125. fn_script_log_fatal "Problem identifying the previous backup directory for exclusion"
  126. core_exit.sh
  127. fi
  128. tar -czf "${backupdir}/${backupname}.tar.gz" -C "${rootdir}" --exclude "${excludedir}" --exclude "${lockdir}/.backup.lock" ./.
  129. local exitcode=$?
  130. if [ ${exitcode} -ne 0 ]; then
  131. fn_print_fail_eol
  132. fn_script_log_fatal "Backup in progress: FAIL"
  133. echo -e "${extractcmd}" | tee -a "${lgsmlog}"
  134. fn_print_fail_nl "Starting backup"
  135. fn_script_log_fatal "Starting backup"
  136. else
  137. fn_print_ok_eol
  138. fn_print_ok_nl "Completed: ${backupname}.tar.gz, total size $(du -sh "${backupdir}/${backupname}.tar.gz" | awk '{print $1}')"
  139. fn_script_log_pass "Backup created: ${backupname}.tar.gz, total size $(du -sh "${backupdir}/${backupname}.tar.gz" | awk '{print $1}')"
  140. fi
  141. # Remove lock file
  142. rm -f "${lockdir:?}/.backup.lock"
  143. }
  144. # Clear old backups according to maxbackups and maxbackupdays variables.
  145. fn_backup_prune(){
  146. # Clear if backup variables are set.
  147. if [ "${maxbackups}" ]&&[ -n "${maxbackupdays}" ]; then
  148. # How many backups there are.
  149. info_distro.sh
  150. # How many backups exceed maxbackups.
  151. backupquotadiff=$((backupcount-maxbackups))
  152. # How many backups exceed maxbackupdays.
  153. backupsoudatedcount=$(find "${backupdir}"/ -type f -name "*.tar.gz" -mtime +"${maxbackupdays}"|wc -l)
  154. # If anything can be cleared.
  155. if [ "${backupquotadiff}" -gt "0" ]||[ "${backupsoudatedcount}" -gt "0" ]; then
  156. fn_print_dots "Pruning"
  157. fn_script_log_info "Backup pruning activated"
  158. fn_print_ok_nl "Pruning"
  159. # If maxbackups greater or equal to backupsoutdatedcount, then it is over maxbackupdays.
  160. if [ "${backupquotadiff}" -ge "${backupsoudatedcount}" ]; then
  161. # Display how many backups will be cleared.
  162. echo -e "* Pruning: ${backupquotadiff} backup(s) has exceeded the ${maxbackups} backups limit"
  163. fn_script_log_info "Pruning: ${backupquotadiff} backup(s) has exceeded the ${maxbackups} backups limit"
  164. fn_sleep_time
  165. fn_print_dots "Pruning: Clearing ${backupquotadiff} backup(s)"
  166. fn_script_log_info "Pruning: Clearing ${backupquotadiff} backup(s)"
  167. # Clear backups over quota.
  168. find "${backupdir}"/ -type f -name "*.tar.gz" -printf '%T@ %p\n' | sort -rn | tail -${backupquotadiff} | cut -f2- -d" " | xargs rm
  169. fn_print_ok_nl "Pruning: Clearing ${backupquotadiff} backup(s)"
  170. fn_script_log_pass "Pruning: Cleared ${backupquotadiff} backup(s)"
  171. # If maxbackupdays is used over maxbackups.
  172. elif [ "${backupquotadiff}" -lt "${backupsoudatedcount}" ]; then
  173. # Display how many backups will be cleared.
  174. echo -e "* Pruning: ${backupsoudatedcount} backup(s) are older than ${maxbackupdays} days."
  175. fn_script_log_info "Pruning: ${backupsoudatedcount} backup(s) older than ${maxbackupdays} days."
  176. fn_sleep_time
  177. fn_print_dots "Pruning: Clearing ${backupquotadiff} backup(s)."
  178. fn_script_log_info "Pruning: Clearing ${backupquotadiff} backup(s)"
  179. # Clear backups over quota
  180. find "${backupdir}"/ -type f -mtime +"${maxbackupdays}" -exec rm -f {} \;
  181. fn_print_ok_nl "Pruning: Clearing ${backupquotadiff} backup(s)"
  182. fn_script_log_pass "Pruning: Cleared ${backupquotadiff} backup(s)"
  183. fi
  184. fi
  185. fi
  186. }
  187. fn_backup_relpath() {
  188. # Written by CedarLUG as a "realpath --relative-to" alternative in bash.
  189. # Populate an array of tokens initialized from the rootdir components.
  190. declare -a rdirtoks=($(readlink -f "${rootdir}" | sed "s/\// /g"))
  191. if [ ${#rdirtoks[@]} -eq 0 ]; then
  192. fn_print_fail_nl "Problem assessing rootdir during relative path assessment"
  193. fn_script_log_fatal "Problem assessing rootdir during relative path assessment: ${rootdir}"
  194. core_exit.sh
  195. fi
  196. # Populate an array of tokens initialized from the backupdir components.
  197. declare -a bdirtoks=($(readlink -f "${backupdir}" | sed "s/\// /g"))
  198. if [ ${#bdirtoks[@]} -eq 0 ]; then
  199. fn_print_fail_nl "Problem assessing backupdir during relative path assessment"
  200. fn_script_log_fatal "Problem assessing backupdir during relative path assessment: ${rootdir}"
  201. core_exit.sh
  202. fi
  203. # Compare the leading entries of each array. These common elements will be clipped off.
  204. # for the relative path output.
  205. for ((base=0; base<${#rdirtoks[@]}; base++))
  206. do
  207. [[ "${rdirtoks[$base]}" != "${bdirtoks[$base]}" ]] && break
  208. done
  209. # Next, climb out of the remaining rootdir location with updir references.
  210. for ((x=base;x<${#rdirtoks[@]};x++))
  211. do
  212. echo -n "../"
  213. done
  214. # Climb down the remaining components of the backupdir location.
  215. for ((x=base;x<$(( ${#bdirtoks[@]} - 1 ));x++))
  216. do
  217. echo -n "${bdirtoks[$x]}/"
  218. done
  219. # In the event there were no directories left in the backupdir above to
  220. # traverse down, just add a newline. Otherwise at this point, there is
  221. # one remaining directory component in the backupdir to navigate.
  222. if (( "$base" < "${#bdirtoks[@]}" )) ; then
  223. echo -e "${bdirtoks[ $(( ${#bdirtoks[@]} - 1)) ]}"
  224. else
  225. echo
  226. fi
  227. }
  228. fn_stop_warning(){
  229. fn_print_warn "Updating server: SteamCMD: ${selfname} will be stopped during backup"
  230. fn_script_log_warn "Updating server: SteamCMD: ${selfname} will be stopped during backup"
  231. totalseconds=3
  232. for seconds in {3..1}; do
  233. fn_print_warn "Updating server: SteamCMD: ${selfname} will be stopped during backup: ${totalseconds}"
  234. totalseconds=$((totalseconds - 1))
  235. sleep 1
  236. if [ "${seconds}" == "0" ]; then
  237. break
  238. fi
  239. done
  240. fn_print_warn_nl "Updating server: SteamCMD: ${selfname} will be stopped during backup"
  241. }
  242. # Restart the server if it was stopped for the backup.
  243. fn_backup_start_server(){
  244. if [ "${serverstopped}" == "yes" ]; then
  245. exitbypass=1
  246. command_start.sh
  247. fi
  248. }
  249. # Run functions.
  250. fn_backup_check_lockfile
  251. fn_backup_create_lockfile
  252. fn_backup_init
  253. fn_backup_stop_server
  254. fn_backup_dir
  255. fn_backup_migrate_olddir
  256. fn_backup_compression
  257. fn_backup_prune
  258. fn_backup_start_server
  259. core_exit.sh