command_backup.sh 9.7 KB

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