command_backup.sh 9.3 KB

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