backup.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. #!/usr/bin/env bash
  2. # Copyright (C) 2013 - 2020 Teddysun <[email protected]>
  3. #
  4. # This file is part of the LAMP script.
  5. #
  6. # LAMP is a powerful bash script for the installation of
  7. # Apache + PHP + MySQL/MariaDB and so on.
  8. # You can install Apache + PHP + MySQL/MariaDB in an very easy way.
  9. # Just need to input numbers to choose what you want to install before installation.
  10. # And all things will be done in a few minutes.
  11. #
  12. # Description: Auto backup shell script
  13. # Description URL: https://teddysun.com/469.html
  14. #
  15. # Website: https://lamp.sh
  16. # Github: https://github.com/teddysun/lamp
  17. #
  18. # You must to modify the config before run it!!!
  19. # Backup MySQL/MariaDB datebases, files and directories
  20. # Backup file is encrypted with AES256-cbc with SHA1 message-digest (option)
  21. # Auto transfer backup file to Google Drive (need install rclone command) (option)
  22. # Auto transfer backup file to FTP server (option)
  23. # Auto delete Google Drive's or FTP server's remote file (option)
  24. [[ $EUID -ne 0 ]] && echo "Error: This script must be run as root!" && exit 1
  25. ########## START OF CONFIG ##########
  26. # Encrypt flag (true: encrypt, false: not encrypt)
  27. ENCRYPTFLG=true
  28. # WARNING: KEEP THE PASSWORD SAFE!!!
  29. # The password used to encrypt the backup
  30. # To decrypt backups made by this script, run the following command:
  31. # openssl enc -aes256 -in [encrypted backup] -out decrypted_backup.tgz -pass pass:[backup password] -d -md sha1
  32. BACKUPPASS="mypassword"
  33. # Directory to store backups
  34. LOCALDIR="/opt/backups/"
  35. # Temporary directory used during backup creation
  36. TEMPDIR="/opt/backups/temp/"
  37. # File to log the outcome of backups
  38. LOGFILE="/opt/backups/backup.log"
  39. # OPTIONAL: If you want backup MySQL database, enter the MySQL root password below
  40. MYSQL_ROOT_PASSWORD=""
  41. # Below is a list of MySQL database name that will be backed up
  42. # If you want backup ALL databases, leave it blank.
  43. MYSQL_DATABASE_NAME[0]=""
  44. # Below is a list of files and directories that will be backed up in the tar backup
  45. # For example:
  46. # File: /data/www/default/test.tgz
  47. # Directory: /data/www/default/test
  48. BACKUP[0]=""
  49. # Number of days to store daily local backups (default 7 days)
  50. LOCALAGEDAILIES="7"
  51. # Delete remote file from Googole Drive or FTP server flag (true: delete, false: not delete)
  52. DELETE_REMOTE_FILE_FLG=false
  53. # Rclone remote name
  54. RCLONE_NAME=""
  55. # Rclone remote folder name (default "")
  56. RCLONE_FOLDER=""
  57. # Upload local file to FTP server flag (true: upload, false: not upload)
  58. FTP_FLG=false
  59. # Upload local file to Google Drive flag (true: upload, false: not upload)
  60. RCLONE_FLG=false
  61. # FTP server
  62. # OPTIONAL: If you want upload to FTP server, enter the Hostname or IP address below
  63. FTP_HOST=""
  64. # FTP username
  65. # OPTIONAL: If you want upload to FTP server, enter the FTP username below
  66. FTP_USER=""
  67. # FTP password
  68. # OPTIONAL: If you want upload to FTP server, enter the username's password below
  69. FTP_PASS=""
  70. # FTP server remote folder
  71. # OPTIONAL: If you want upload to FTP server, enter the FTP remote folder below
  72. # For example: public_html
  73. FTP_DIR=""
  74. ########## END OF CONFIG ##########
  75. # Date & Time
  76. DAY=$(date +%d)
  77. MONTH=$(date +%m)
  78. YEAR=$(date +%C%y)
  79. BACKUPDATE=$(date +%Y%m%d%H%M%S)
  80. # Backup file name
  81. TARFILE="${LOCALDIR}""$(hostname)"_"${BACKUPDATE}".tgz
  82. # Encrypted backup file name
  83. ENC_TARFILE="${TARFILE}.enc"
  84. # Backup MySQL dump file name
  85. SQLFILE="${TEMPDIR}mysql_${BACKUPDATE}.sql"
  86. log() {
  87. echo "$(date "+%Y-%m-%d %H:%M:%S")" "$1"
  88. echo -e "$(date "+%Y-%m-%d %H:%M:%S")" "$1" >> ${LOGFILE}
  89. }
  90. # Check for list of mandatory binaries
  91. check_commands() {
  92. # This section checks for all of the binaries used in the backup
  93. BINARIES=( cat cd du date dirname echo openssl mysql mysqldump pwd rm tar )
  94. # Iterate over the list of binaries, and if one isn't found, abort
  95. for BINARY in "${BINARIES[@]}"; do
  96. if [ ! "$(command -v "$BINARY")" ]; then
  97. log "$BINARY is not installed. Install it and try again"
  98. exit 1
  99. fi
  100. done
  101. # check rclone command
  102. RCLONE_COMMAND=false
  103. if [ "$(command -v "rclone")" ]; then
  104. RCLONE_COMMAND=true
  105. fi
  106. # check ftp command
  107. if ${FTP_FLG}; then
  108. if [ ! "$(command -v "ftp")" ]; then
  109. log "ftp is not installed. Install it and try again"
  110. exit 1
  111. fi
  112. fi
  113. }
  114. calculate_size() {
  115. local file_name=$1
  116. local file_size=$(du -h $file_name 2>/dev/null | awk '{print $1}')
  117. if [ "x${file_size}" = "x" ]; then
  118. echo "unknown"
  119. else
  120. echo "${file_size}"
  121. fi
  122. }
  123. # Backup MySQL databases
  124. mysql_backup() {
  125. if [ -z "${MYSQL_ROOT_PASSWORD}" ]; then
  126. log "MySQL root password not set, MySQL backup skipped"
  127. else
  128. log "MySQL dump start"
  129. mysql -u root -p"${MYSQL_ROOT_PASSWORD}" 2>/dev/null <<EOF
  130. exit
  131. EOF
  132. if [ $? -ne 0 ]; then
  133. log "MySQL root password is incorrect. Please check it and try again"
  134. exit 1
  135. fi
  136. if [ "${MYSQL_DATABASE_NAME[*]}" == "" ]; then
  137. mysqldump -u root -p"${MYSQL_ROOT_PASSWORD}" --all-databases > "${SQLFILE}" 2>/dev/null
  138. if [ $? -ne 0 ]; then
  139. log "MySQL all databases backup failed"
  140. exit 1
  141. fi
  142. log "MySQL all databases dump file name: ${SQLFILE}"
  143. #Add MySQL backup dump file to BACKUP list
  144. BACKUP=(${BACKUP[*]} ${SQLFILE})
  145. else
  146. for db in ${MYSQL_DATABASE_NAME[*]}; do
  147. unset DBFILE
  148. DBFILE="${TEMPDIR}${db}_${BACKUPDATE}.sql"
  149. mysqldump -u root -p"${MYSQL_ROOT_PASSWORD}" ${db} > "${DBFILE}" 2>/dev/null
  150. if [ $? -ne 0 ]; then
  151. log "MySQL database name [${db}] backup failed, please check database name is correct and try again"
  152. exit 1
  153. fi
  154. log "MySQL database name [${db}] dump file name: ${DBFILE}"
  155. #Add MySQL backup dump file to BACKUP list
  156. BACKUP=(${BACKUP[*]} ${DBFILE})
  157. done
  158. fi
  159. log "MySQL dump completed"
  160. fi
  161. }
  162. start_backup() {
  163. [ "${#BACKUP[@]}" -eq 0 ] && echo "Error: You must to modify the [$(basename $0)] config before run it!" && exit 1
  164. log "Tar backup file start"
  165. tar -zcPf ${TARFILE} ${BACKUP[@]}
  166. if [ $? -gt 1 ]; then
  167. log "Tar backup file failed"
  168. exit 1
  169. fi
  170. log "Tar backup file completed"
  171. # Encrypt tar file
  172. if ${ENCRYPTFLG}; then
  173. log "Encrypt backup file start"
  174. openssl enc -aes256 -in "${TARFILE}" -out "${ENC_TARFILE}" -pass pass:"${BACKUPPASS}" -md sha1
  175. log "Encrypt backup file completed"
  176. # Delete unencrypted tar
  177. log "Delete unencrypted tar file: ${TARFILE}"
  178. rm -f ${TARFILE}
  179. fi
  180. # Delete MySQL temporary dump file
  181. for sql in $(ls ${TEMPDIR}*.sql); do
  182. log "Delete MySQL temporary dump file: ${sql}"
  183. rm -f ${sql}
  184. done
  185. if ${ENCRYPTFLG}; then
  186. OUT_FILE="${ENC_TARFILE}"
  187. else
  188. OUT_FILE="${TARFILE}"
  189. fi
  190. log "File name: ${OUT_FILE}, File size: $(calculate_size ${OUT_FILE})"
  191. }
  192. # Transfer backup file to Google Drive
  193. # If you want to install rclone command, please visit website:
  194. # https://rclone.org/downloads/
  195. rclone_upload() {
  196. if ${RCLONE_FLG} && ${RCLONE_COMMAND}; then
  197. [ -z "${RCLONE_NAME}" ] && log "Error: RCLONE_NAME can not be empty!" && return 1
  198. if [ -n "${RCLONE_FOLDER}" ]; then
  199. rclone ls ${RCLONE_NAME}:${RCLONE_FOLDER} 2>&1 > /dev/null
  200. if [ $? -ne 0 ]; then
  201. log "Create the path ${RCLONE_NAME}:${RCLONE_FOLDER}"
  202. rclone mkdir ${RCLONE_NAME}:${RCLONE_FOLDER}
  203. fi
  204. fi
  205. log "Tranferring backup file: ${OUT_FILE} to Google Drive"
  206. rclone copy ${OUT_FILE} ${RCLONE_NAME}:${RCLONE_FOLDER} >> ${LOGFILE}
  207. if [ $? -ne 0 ]; then
  208. log "Error: Tranferring backup file: ${OUT_FILE} to Google Drive failed"
  209. return 1
  210. fi
  211. log "Tranferring backup file: ${OUT_FILE} to Google Drive completed"
  212. fi
  213. }
  214. # Tranferring backup file to FTP server
  215. ftp_upload() {
  216. if ${FTP_FLG}; then
  217. [ -z "${FTP_HOST}" ] && log "Error: FTP_HOST can not be empty!" && return 1
  218. [ -z "${FTP_USER}" ] && log "Error: FTP_USER can not be empty!" && return 1
  219. [ -z "${FTP_PASS}" ] && log "Error: FTP_PASS can not be empty!" && return 1
  220. [ -z "${FTP_DIR}" ] && log "Error: FTP_DIR can not be empty!" && return 1
  221. local FTP_OUT_FILE=$(basename ${OUT_FILE})
  222. log "Tranferring backup file: ${FTP_OUT_FILE} to FTP server"
  223. ftp -in ${FTP_HOST} 2>&1 >> ${LOGFILE} <<EOF
  224. user $FTP_USER $FTP_PASS
  225. binary
  226. lcd $LOCALDIR
  227. cd $FTP_DIR
  228. put $FTP_OUT_FILE
  229. quit
  230. EOF
  231. if [ $? -ne 0 ]; then
  232. log "Error: Tranferring backup file: ${FTP_OUT_FILE} to FTP server failed"
  233. return 1
  234. fi
  235. log "Tranferring backup file: ${FTP_OUT_FILE} to FTP server completed"
  236. fi
  237. }
  238. # Get file date
  239. get_file_date() {
  240. #Approximate a 30-day month and 365-day year
  241. DAYS=$(( $((10#${YEAR}*365)) + $((10#${MONTH}*30)) + $((10#${DAY})) ))
  242. unset FILEYEAR FILEMONTH FILEDAY FILEDAYS FILEAGE
  243. FILEYEAR=$(echo "$1" | cut -d_ -f2 | cut -c 1-4)
  244. FILEMONTH=$(echo "$1" | cut -d_ -f2 | cut -c 5-6)
  245. FILEDAY=$(echo "$1" | cut -d_ -f2 | cut -c 7-8)
  246. if [[ "${FILEYEAR}" && "${FILEMONTH}" && "${FILEDAY}" ]]; then
  247. #Approximate a 30-day month and 365-day year
  248. FILEDAYS=$(( $((10#${FILEYEAR}*365)) + $((10#${FILEMONTH}*30)) + $((10#${FILEDAY})) ))
  249. FILEAGE=$(( 10#${DAYS} - 10#${FILEDAYS} ))
  250. return 0
  251. fi
  252. return 1
  253. }
  254. # Delete Google Drive's old backup file
  255. delete_gdrive_file() {
  256. local FILENAME=$1
  257. if ${DELETE_REMOTE_FILE_FLG} && ${RCLONE_COMMAND}; then
  258. rclone ls ${RCLONE_NAME}:${RCLONE_FOLDER}/${FILENAME} 2>&1 > /dev/null
  259. if [ $? -eq 0 ]; then
  260. rclone delete ${RCLONE_NAME}:${RCLONE_FOLDER}/${FILENAME} >> ${LOGFILE}
  261. if [ $? -eq 0 ]; then
  262. log "Google Drive's old backup file: ${FILENAME} has been deleted"
  263. else
  264. log "Failed to delete Google Drive's old backup file: ${FILENAME}"
  265. fi
  266. else
  267. log "Google Drive's old backup file: ${FILENAME} is not exist"
  268. fi
  269. fi
  270. }
  271. # Delete FTP server's old backup file
  272. delete_ftp_file() {
  273. local FILENAME=$1
  274. if ${DELETE_REMOTE_FILE_FLG} && ${FTP_FLG}; then
  275. ftp -in ${FTP_HOST} 2>&1 >> ${LOGFILE} <<EOF
  276. user $FTP_USER $FTP_PASS
  277. cd $FTP_DIR
  278. del $FILENAME
  279. quit
  280. EOF
  281. if [ $? -eq 0 ]; then
  282. log "FTP server's old backup file: ${FILENAME} has been deleted"
  283. else
  284. log "Failed to delete FTP server's old backup file: ${FILENAME}"
  285. fi
  286. fi
  287. }
  288. # Clean up old file
  289. clean_up_files() {
  290. cd ${LOCALDIR} || exit
  291. if ${ENCRYPTFLG}; then
  292. LS=($(ls *.enc))
  293. else
  294. LS=($(ls *.tgz))
  295. fi
  296. for f in ${LS[@]}; do
  297. get_file_date ${f}
  298. if [ $? -eq 0 ]; then
  299. if [[ ${FILEAGE} -gt ${LOCALAGEDAILIES} ]]; then
  300. rm -f ${f}
  301. log "Old backup file name: ${f} has been deleted"
  302. delete_gdrive_file ${f}
  303. delete_ftp_file ${f}
  304. fi
  305. fi
  306. done
  307. }
  308. # Main progress
  309. STARTTIME=$(date +%s)
  310. # Check if the backup folders exist and are writeable
  311. [ ! -d "${LOCALDIR}" ] && mkdir -p ${LOCALDIR}
  312. [ ! -d "${TEMPDIR}" ] && mkdir -p ${TEMPDIR}
  313. log "Backup progress start"
  314. check_commands
  315. mysql_backup
  316. start_backup
  317. log "Backup progress complete"
  318. log "Upload progress start"
  319. rclone_upload
  320. ftp_upload
  321. log "Upload progress complete"
  322. log "Cleaning up"
  323. clean_up_files
  324. ENDTIME=$(date +%s)
  325. DURATION=$((ENDTIME - STARTTIME))
  326. log "All done"
  327. log "Backup and transfer completed in ${DURATION} seconds"