backup.sh 10 KB

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