#!/usr/bin/env bash # MySQL-BU - MySQL Backup Scripts action="$1" FOLDER='/opt/idssys/mysqlbu' VERS='2.23-09202025' source /opt/idssys/defaults/colors.inc source /opt/idssys/defaults/default.inc source /opt/idssys/mysqlbu/settings.conf # ========================================================= # if [ $(id -u) != "0" ]; then echo "Sorry bitch, I need root privileges!" exit 1 fi MYSQLDATA="`grep --perl-regexp --only-match '(?<=datadir=).+' "$MYSQLCFG"`" DAY_BACKUP_DIR="${BACKUP_PATH}/`date +%Y-%m-%d`/" [ ! -d $DAY_BACKUP_DIR ] && mkdir -p $DAY_BACKUP_DIR BASE_BACKUP="`ls -1 $DAY_BACKUP_DIR 2> /dev/null | grep --max-count=1 --perl-regexp '^.+_BASE'`" # LATEST_BACKUP="`ls -1t $DAY_BACKUP_DIR | grep --max-count=1 --perl-regexp '^.+-'`" LIST_BACKUPS(){ mysqlbu list read -n 1 -s -p "Press any key to continue" } RUN_BACKUP(){ mysqlbu backup echo "" read -n 1 -s -p "Press any key to continue" } ADD_CRONTAB(){ (crontab -l ; echo "0 */3 * * * /usr/local/bin/mysqlbu backup")| crontab - echo "" echo -e "${idsCL[Green]}The crontab entry has been made${idsCL[Default]}" echo "" read -n 1 -s -p "Press any key to continue" } DELETE_BACKUP() { if [ "$1" = "M" ] || [ "$1" = "m" ]; then MAIN_MENU; elif [ "$1" = "Q" ] || [ "$1" = "q" ]; then EXIT1; fi declare -A tbus tba=1 for day in $BACKUP_PATH/* ; do tbus[${tba}]=$day tbb=0 for hour in $day/* ; do tbus[${tba}${abet[${tbb}]}]=$hour tbb=`expr $tbb + 1` done tba=`expr $tba + 1` done [ ! ${tbus[$1]+abc} ] && { echo -e -n "${idsCL[LightRed]}Incorrect entry, please try again... (press any key)${idsCL[Default]}"; read -n 1 DELETE_MENU } sbu1=${tbus[$1]} thr=`echo ${sbu1//_BASE/} | sed 's/.*\///'` bsz=`du -sh ${sbu1} | awk '{print $1}'` IFS=/ sbu1a=(${tbus[$1]}) unset IFS echo "" if [ ${#1} = 1 ] ; then echo -e "Backup selected: ${idsST[Bold]}${idsCL[Red]}`date -d"${sbu1a[4]}" +"%A, %B %d, %Y"` - (Entire Day) - (${bsz})${idsST[Reset]}" echo -e "${idsCL[Red]}(This will DELETE the entire days worth of backups for the date selected.)"${idsCL[Default]} delbu=${sbu1} elif [ "${sbu1a[5]: -5}" = "_BASE" ]; then echo -e "Backup selected: ${idsST[Bold]}${idsCL[Red]}`date -d"${sbu1a[4]} ${thr//-/:}" +"%A, %B %d, %Y - %H:%M"` - Full Backup - (${bsz})${idsST[Reset]}" echo -e "${idsCL[Red]}(This is a daily base backup, and will result in the full day being DELETED.)"${idsCL[Default]} delbu="/${sbu1a[1]}/${sbu1a[2]}/${sbu1a[3]}/${sbu1a[4]}" else echo -e "Backup selected: ${idsST[Bold]}${idsCL[Red]}`date -d"${sbu1a[4]} ${thr//-/:}" +"%A, %B %d, %Y - %H:%M"` - Incremental - (${bsz})${idsST[Reset]}" delbu=${sbu1} fi read -p "Are you sure you wish to DELETE this backup (y/N)?" choice case "$choice" in [Yy]) rm -rf $delbu #echo "rm -rf $delbu" echo "" echo -e "${idsCL[LightRed]}Backup has been deleted${idsCL[Default]}" read -n 1 -s -p "Press any key to continue";; * ) DELETE_MENU;; esac } MAIN_MENU() { while : do clear echo "" echo -e "${idsCL[LightGreen]} MySQL-BU - M y S Q L B A C K U P S C R I P T S${idsCL[Default]} ${idsCL[DarkGray]}(ver-${VERS})${idsCL[Default]}" echo -e "${idsCL[LightGreen]}---------------------------------------------------------------------------${idsCL[Default]}" echo -e "Backups Location: ${idsCL[LightBlue]}${BACKUP_PATH}${idsCL[Default]}" echo -e "MySQL User: ${idsCL[LightBlue]}${MYSQLUSER}${idsCL[Default]}" echo -e "MySQL Password: ${idsCL[LightBlue]}****${idsCL[Default]}" echo -e "MySQL Config File: ${idsCL[LightBlue]}${MYSQLCFG}${idsCL[Default]}" echo -e "# of Days to Keep: ${idsCL[LightBlue]}${BACKUPSTOKEEP}${idsCL[Default]}" echo -e "${idsCL[LightGreen]}---------------------------------------------------------------------------${idsCL[Default]}" echo "" echo " [1] Change Backup Location" echo " [2] Change MySQL User" echo " [3] Change MySQL Password" echo " [4] Change MySQL Config File Location" echo " [5] Change The Number of Backup Days to Keep" echo "" echo " [B] Run Backup" echo " [L] List Backups" echo " [D] Delete Backups" echo "" echo " [C] Add Crontab Entry to run every 3 Hours" echo -e " ${idsCL[DarkGray]}(edit crontab manually to adjust the backup frequency)${idsCL[Default]}" echo "" echo " [Q] Quit" echo "" echo "" echo -e -n "${idsCL[LightYellow]}Please select an [ActionItem] from above:${idsCL[Default]} " read -n 1 opt echo "" case $opt in 1) WEBMIN_INSTALL INSTALL_MENU;; 2) GLANCES_INSTALL INSTALL_MENU;; 3) X11VNC_INSTALL INSTALL_MENU;; 4) FW_INSTALL INSTALL_MENU;; 5) LINUPX_UNINSTALL INSTALL_MENU;; [Bb]) RUN_BACKUP;; [Ll]) LIST_BACKUPS;; [Dd]) DELETE_MENU;; [Cc]) ADD_CRONTAB;; [Qq]) EXIT1 exit 1;; *) echo "Thats an invaild option,"; echo "please select a valid option only."; sleep 1;; esac done } DELETE_MENU() { while : do clear echo "" echo -e "${idsCL[LightGreen]} MySQL-BU - M y S Q L B A C K U P S C R I P T S${idsCL[Default]} ${idsCL[DarkGray]}(ver-${VERS})${idsCL[Default]}" echo -e "${idsCL[LightGreen]}---------------------------------------------------------------------------${idsCL[Default]}" echo "" echo -e "${idsCL[LightYellow]}========== Select Which Backup to Delete ==========${idsCL[Default]}" tba=1 for day in $BACKUP_PATH/* ; do if [ "${day}" != "${BACKUP_PATH}/latest" ]; then #tdt=`sed 's/.*\///' <<< $day` tdt=`echo $day | sed 's/.*\///'` bsz=`du -sh ${day} | awk '{print $1}'` echo -e "${idsBG[Blue]}${idsCL[LightYellow]}(${tba})${idsCL[White]} -> `date -d"${tdt}" +"%A, %B %d, %Y"` - ${bsz} ${idsCL[Default]}${idsBG[Default]}" tbb=0 for hour in $day/* ; do #thr=`sed 's/.*\///' <<< ${hour//_BASE/}` thr=`echo ${hour//_BASE/} | sed 's/.*\///'` bsz=`du -sh ${hour} | awk '{print $1}'` if [ ${hour: -5} = "_BASE" ]; then echo -e "${idsCL[LightYellow]}(${tba}${abet[${tbb}]^^})${idsCL[LightGreen]}${idsST[Bold]} -> `date -d"${tdt} ${thr//-/:}" +"%H:%M"` - FULL BACKUP${idsST[Reset]}${idsCL[LightGreen]} - ${bsz}${idsCL[Default]}" else echo -e "${idsCL[LightYellow]}(${tba}${abet[${tbb}]^^})${idsCL[Cyan]} -> `date -d"${tdt} ${thr//-/:}" +"%H:%M"` - Incremental - ${bsz}${idsCL[Default]}" fi tbb=`expr $tbb + 1` done tba=`expr $tba + 1` fi done echo -e "${idsCL[Yellow]}===================================================${idsCL[Default]}" echo "" echo "[M] Main Menu" echo "[Q] Quit" echo "" echo "" echo -e -n "${idsCL[LightYellow]}Please select a backup from above:${idsCL[Default]} " read -n 2 opt echo "" DELETE_BACKUP $opt done } case $1 in 'backup') if [ "$BACKUP_ServerName" != "" ] || [ "${BACKUP_MOUNTED}" == "true" ]; then echo "$(date) - Checking for mounted Backup Drive..." if [ ! -f ${BACKUP_PATH}/mounted ]; then echo "$(date) - Not found, mounting drive now..." # mount.cifs ${BACKUP_ServerName} ${BACKUP_PATH} -o user=${BACKUP_UserName},password=${BACKUP_Password} umount ${BACKUP_PATH} rm -Rf ${BACKUP_PATH} mkdir -p ${BACKUP_PATH} mount -a c=0 until [ $c -gt 179 ]; do if [ -f ${BACKUP_PATH}/mounted ]; then echo "$(date) - Backup Drive mounted" c=10000 else ((c++)) sleep 1s fi done if [ $c != 10000 ]; then echo "$(date) - Backup Drive COULD NOT be mounted... exiting" exit 1 fi else echo "$(date) - Backup Drive already mounted" fi fi ulimit -n 2048 if [ "${BACKUP_TYPE}" == "xtrabackup" ] || [ "${BACKUP_TYPE}" == "both" ]; then [ "$BASE_BACKUP" = "" ] && $0 backup-base || $0 backup-incremental fi if [ "${BACKUP_TYPE}" == "mariadbdump" ] || [ "${BACKUP_TYPE}" == "mysqldump" ] || [ "${BACKUP_TYPE}" == "both" ]; then /usr/local/bin/mysqlbu mariadbdump fi # LATEST_BACKUP="`ls -1t $DAY_BACKUP_DIR | grep --max-count=1 --perl-regexp '^.+-'`" # ln -sfn ${DAY_BACKUP_DIR/${BACKUP_PATH}/.}${LATEST_BACKUP} ${BACKUP_PATH}/latest allfiles=`ls -1 ${BACKUP_PATH}` files=(`ls -1 ${BACKUP_PATH} | tail -$((${BACKUPSTOKEEP}+2))`) for i in $allfiles; do keep=0; for a in ${files[@]}; do if [ $i == $a ]; then keep=1; break fi; done; if [ $keep == 0 ]; then rm -rf ${BACKUP_PATH}/$i; fi; done ;; 'backup-base') DEST_DIR="${DAY_BACKUP_DIR}`date +%H-%M-%S`_BASE" if [ "$BASE_BACKUP" != "" ]; then echo -e "$(date) - ${idsCL[Green]}Daily base backup already done. Running incremental backup...${idsCL[Default]}" sleep 3 $0 backup-incremental exit 1 fi if [ ! -d "$DAY_BACKUP_DIR" ]; then mkdir -p "$DAY_BACKUP_DIR" fi if [ ! -z ${BACKUP_HOST_IP+x} ]; then BACKUPHOSTIP="--host=${BACKUP_HOST_IP}"; else BACKUPHOSTIP=""; fi if [ ! -z ${BACKUP_HOST_PORT+x} ]; then BACKUPHOSTPORT="--port=${BACKUP_HOST_PORT}"; else BACKUPHOSTPORT=""; fi /bin/mariadb --execute "SET GLOBAL wsrep_desync = ON;" # innobackupex --defaults-file="$MYSQLCFG" --user="$MYSQLUSER" --password="$MYSQLPASS" --no-timestamp "$DEST_DIR" ${BACKUP_DAEMON} --backup ${BACKUPHOSTIP} ${BACKUPHOSTPORT} --target-dir="$DEST_DIR" --user="$MYSQLUSER" --password="$MYSQLPASS" /bin/mariadb --execute "SET GLOBAL wsrep_desync = OFF;" # ln -sfn ${DAY_BACKUP_DIR}${BASE_BACKUP} ${BACKUP_PATH}/latest ;; 'backup-incremental') DEST_DIR="${DAY_BACKUP_DIR}`date +%H-%M-%S`" if [ "$BASE_BACKUP" = "" ]; then echo -e "$(date) - ${idsCL[Green]}Daily base backup not found. Running base backup...${idsCL[Default]}" $0 backup-base exit 1 fi if [ ! -z ${BACKUP_HOST_IP+x} ]; then BACKUPHOSTIP="--host=${BACKUP_HOST_IP}"; else BACKUPHOSTIP=""; fi if [ ! -z ${BACKUP_HOST_PORT+x} ]; then BACKUPHOSTPORT="--port=${BACKUP_HOST_PORT}"; else BACKUPHOSTPORT=""; fi /bin/mariadb --execute "SET GLOBAL wsrep_desync = ON;" #innobackupex --defaults-file="$MYSQLCFG" --user="$MYSQLUSER" --password="$MYSQLPASS" \ #--incremental-basedir="${DAY_BACKUP_DIR}${BASE_BACKUP}" \ #--incremental "$DEST_DIR" \ #--no-timestamp ${BACKUP_DAEMON} --backup ${BACKUPHOSTIP} ${BACKUPHOSTPORT} --target-dir="$DEST_DIR" --incremental-basedir="${DAY_BACKUP_DIR}${BASE_BACKUP}" --user="$MYSQLUSER" --password="$MYSQLPASS" /bin/mariadb --execute "SET GLOBAL wsrep_desync = OFF;" # ln -sfn ${LATEST_BACKUP} ${BACKUP_PATH}/latest ;; 'mysqldump'|'mariadbdump') DEST_DIR="${DAY_BACKUP_DIR}`date +%H-%M-%S`-mysqldump" if [ ! -d "$DEST_DIR" ]; then mkdir -p "$DEST_DIR" fi if [ ! -z ${BACKUP_HOST_IP+x} ]; then BACKUPHOSTIP="--host=${BACKUP_HOST_IP}"; else BACKUPHOSTIP=""; fi if [ ! -z ${BACKUP_HOST_PORT+x} ]; then BACKUPHOSTPORT="--port=${BACKUP_HOST_PORT}"; else BACKUPHOSTPORT=""; fi DATABASES=$(/usr/bin/mariadb -u ${MYSQLUSER} --password="${MYSQLPASS}" -e "SHOW DATABASES;" | tr -d "| " | grep -v Database) /bin/mariadb --execute "SET GLOBAL wsrep_desync = ON;" for db in $DATABASES; do echo -en "$(date) - Dumping the '${idsCL[LightCyan]}$db${idsCL[Default]}' table ... " FILE="${DEST_DIR}/$db.sql.gz" /bin/mariadb-dump --single-transaction --routines --master-data --flush-logs --quick -u ${MYSQLUSER} --password="${MYSQLPASS}" -B $db | gzip > "$FILE" echo -e "${idsCL[Green]}Done${idsCL[Default]}" done /bin/mariadb --execute "SET GLOBAL wsrep_desync = OFF;" ;; 'restore') if [ "`ps aux | grep --only-match mysqld`" != "" ]; then read -p "I think mysql-server still running. It's STRONGLY recommended to stop it before restoring any backup to avoid corrupted files. Do you want to continue anyway? [Y/N]" yn case $yn in [Yy]* ) break;; * ) exit;; esac fi REQUEST_BACKUP="${BACKUP_PATH}/${2}/${3}" BASE_BACKUP="${BACKUP_PATH}/${2}/`ls ${BACKUP_PATH}/${2} | grep --max-count=1 --perl-regexp '^.+_BASE'`" TMP_BACKUP="${BACKUP_PATH}/${2}/.current_backup" if [ ! -d "$REQUEST_BACKUP" ] || [ ! $2 ] || [ ! $3 ]; then echo -e "${idsCL[Red]}Backup not found!${idsCL[Default]}" echo "Usage: $0 restore 2019-01-05 13-00-10" exit 1 fi if [ -d "$MYSQLDATA" ]; then mv "$MYSQLDATA" "`grep --perl-regexp --only-match '^.*(?=/$)' <<< ${MYSQLDATA}`_restore-`date +%Y-%m-%d_%H-%M-%S`" mkdir "$MYSQLDATA" fi if [ -d "$TMP_BACKUP" ]; then rm -rf "$TMP_BACKUP" fi cp --recursive "$BASE_BACKUP" "$TMP_BACKUP" innobackupex --apply-log --redo-only "$TMP_BACKUP" if [ REQUEST_BACKUP != BASE_BACKUP ]; then innobackupex --apply-log "$TMP_BACKUP" --incremental-dir="$REQUEST_BACKUP" fi innobackupex --apply-log "$TMP_BACKUP" innobackupex --copy-back "$TMP_BACKUP" chown -R mysql:mysql "$MYSQLDATA" rm -rf "$TMP_BACKUP" ;; 'list') clear echo "" echo -e " ${idsCL[LightGreen]}MySQL-BU - M y S Q L B A C K U P S C R I P T S${idsCL[Default]} ${idsCL[DarkGray]}(ver-${VERS})${idsCL[Default]}" echo -e "${idsCL[LightGreen]}---------------------------------------------------------------------------${idsCL[Default]}" echo "" echo -e "${idsCL[LightYellow]}============ Available MySQL Backups ============${idsCL[Default]}" bsz=`du -sh ${BACKUP_PATH} | awk '{print $1}'` echo -e "${idsCL[Yellow]}Storage space used by all backups - ${bsz} ${idsCL[Default]}" echo -e "${idsCL[LightYellow]}-------------------------------------------------${idsCL[Default]}" for day in $BACKUP_PATH/* ; do if [ "${day}" != "${BACKUP_PATH}/latest" ] && [ "${day}" != "${BACKUP_PATH}/mounted" ]; then tdt=`sed 's/.*\///' <<< $day` bsz=`du -sh ${day} | awk '{print $1}'` echo -e "${idsBG[Blue]}${idsCL[White]}-> `date -d"${tdt}" +"%a, %B %d, %Y"` --- ${bsz} ${idsCL[Default]}${idsBG[Default]}" for hour in $day/* ; do thr=`sed 's/.*\///' <<< ${hour//_BASE/}` thr=`sed 's/-mysqldump//g' <<< ${thr}` bsz=`du -sh ${hour} | awk '{print $1}'` if [ ${hour: -5} = "_BASE" ]; then echo -e "${idsCL[LightGreen]}${idsST[Bold]} -> `date -d"${tdt} ${thr//-/:}" +"%H:%M"` - Full File Backup${idsST[Reset]}${idsCL[LightGreen]} |- ${bsz}${idsCL[Default]}" elif [[ "${hour}" = *"-mysqldump"* ]]; then echo -e "${idsCL[LightGreen]}${idsST[Bold]} -> `date -d"${tdt} ${thr//-/:}" +"%H:%M"` - Full SQL Dump${idsST[Reset]}${idsCL[LightGreen]} |- ${bsz}${idsCL[Default]}" # echo -e "${idsCL[Cyan]} -> `date -d"${tdt} ${thr//-/:}" +"%H:%M"` - Full SQL Dump |- ${bsz}${idsCL[Default]}" else echo -e "${idsCL[Cyan]} -> `date -d"${tdt} ${thr//-/:}" +"%H:%M"` - Incremental File |- ${bsz}${idsCL[Default]}" fi done fi done echo -e "${idsCL[Yellow]}=================================================${idsCL[Default]}" echo "" ;; 'gui') MAIN_MENU ;; 'delete') DELETE_MENU ;; 'delold') allfiles=`ls -1 ${BACKUP_PATH}` files=(`ls -1 ${BACKUP_PATH} | tail -$((${BACKUPSTOKEEP}+2))`) for i in $allfiles; do keep=0; for a in ${files[@]}; do if [ $i == $a ]; then keep=1; break fi; done; if [ $keep == 0 ]; then rm -rf ${BACKUP_PATH}/$i; # echo "DEL: ${BACKUP_PATH}/$i" fi; done ;; *) echo "Usage: $0 { backup | backup-base | backup-incremental | restore | list | gui }" ;; esac exit 0