backup.sh 12 KB

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