File: //ibin/ibackup
#!/bin/bash
#
# ibackup
#
# Written by - Jack Sasportas - Jack@innovativeinternet.com
#
# Backup Data Files based on SOURCE values utilizing rsync and hard links and cleans up files older than
# 90 days using the cleanup script.
#
# Usage: ibackup [backup_type] [destination] [debug]
#
# [backup_type] - data type: data, db, config
# [destination] - backup destination: local, external, remote
# [debug] - Optional: pass "debug" to display the command without executing
#
# local - creates backup on the calling server
# external - creates backup in the local data center on a different server
# remote - creates backup in the remote data center
#
# Color codes:
# light blue - informational
# navy blue - executed command
# purple - backup communications
# red - error
#
#########################################################################################################
# Version 5.2
#
# Updated:
# 10-18-2024 - Enhanced email notifications to include detailed information
# 10-18-2024 - Added error handling and enhanced email notifications
# 10-18-2024 - Script renamed to 'ibackup', moved server configurations to /etc/ibackup_config.sh
# Made server configuration loading dynamic to improve efficiency
# 12-12-2023 - Changed gallardo to firebird as remote server.
# 07-04-2023 - Added code for different cleanup for external DB files vs folders
# 06-29-2018 - Added debug functionality with "debug" parameter
# 06-27-2017 - cPanel customizations
# 03-08-2018 - Fixed backup path
# 03-14-2018 - Fixed deletion of log files
# 12-23-2019 - Added Disk Check Function with email notification
# 12-01-2020 - Enhanced Email Notification
# 12-29-2021 - Modified database folder to db
#
#########################################################################################################
# Source configuration file
CONFIG_FILE='/ibin/ibackup_config.sh'
if [[ ! -f $CONFIG_FILE ]]; then
echo "Configuration file $CONFIG_FILE not found!"
exit 1
fi
source $CONFIG_FILE
# Function to send email notifications
send_email() {
local subject="$1"
local message="$2"
echo -e "$message" | mail -s "$subject" -r "$FROM_EMAIL" "$NOTIFICATION_EMAIL"
}
backup_list() {
local home_directory="/home"
local output_file="/ibin/cpbackup_source.txt"
if [[ ! -d "$home_directory" ]]; then
echo "Home directory $home_directory does not exist!"
exit 1
fi
cd "$home_directory" || exit 1
find . -mindepth 1 -maxdepth 1 -type d -printf "$home_directory/%f\n" > "$output_file"
echo "Folder list has been saved to $output_file"
}
# Color codes
LBLUE='\e[104m'
PURPLE='\e[37;45m'
NBLUE='\e[37;44m'
RED='\e[41m'
CLEAR='\033[0m\n'
date=$(date "+%Y-%m-%d_%H%M%S")
readable_date=$(date)
SSERVER=$(hostname | perl -p -e 's/(.*?)\..*/$1/')
BACKUP_LOG="/var/log/cpbackup/${SSERVER}-$1-$2-$date"
BACKUP_SOURCE='/ibin/cpbackup_source.txt'
BACKUP_EXCLUDE='/ibin/cpbackup_exclude.txt'
BACKUP_FOLDER='/backup'
# Notification Variables
NOTIFICATION_EMAIL='jack@innovativeinternet.com'
FROM_EMAIL='BackupAdmin@InnovativeInternet.net'
BACKUP_DISK='/backup'
BACKUP_DISK_USED=$(df -Ph | grep $BACKUP_DISK | awk '{print $5}' | tr -d '%')
MAX=90
OPTS='-e ssh -aP --delete --ignore-errors'
# Check Disk Space before executing Job
if [[ $BACKUP_DISK_USED -ge $MAX ]]; then
subject="Backup Job on $SSERVER Stopped - Disk Full ($BACKUP_DISK_USED%)"
message="Backup Job on $SSERVER STOPPED!\nRun: $readable_date\nDisk usage on $BACKUP_DISK is at $BACKUP_DISK_USED%.\nPlease free up space."
send_email "$subject" "$message"
echo -e "${RED}Backup Job ABORTED on $SSERVER because Disk $BACKUP_DISK is $BACKUP_DISK_USED% full!${CLEAR}"
exit 1
fi
case $SSERVER in
maggie|g2|garage)
echo -e "${LBLUE}Skipping backup list generation for $SSERVER${CLEAR}"
;;
*)
backup_list
;;
esac
# Determine Backup Server from configuration
echo -e "${LBLUE}Determining Backup Server${CLEAR}"
# Build variable names based on server name
locserver_var="${SSERVER}_locserver"
extserver_var="${SSERVER}_extserver"
remserver_var="${SSERVER}_remserver"
uniqdns_var="${SSERVER}_uniqdns"
# Check if the variables exist
if [[ -z "${!locserver_var}" ]]; then
echo -e "${RED}EXIT: No Matching Server Configuration for $SSERVER${CLEAR}"
exit 1
fi
# Assign the variables dynamically using variable indirection
LocServer="${!locserver_var}"
ExtSERVER="${!extserver_var}"
RemSERVER="${!remserver_var}"
UNIQDNS="${!uniqdns_var}"
# Determine Backup Type, Source, and Prefix
case $1 in
data)
if [[ ! -f $BACKUP_SOURCE ]]; then
echo -e "${RED}Backup source file $BACKUP_SOURCE not found!${CLEAR}"
exit 1
fi
SOURCE=$(cat $BACKUP_SOURCE)
TYPE='data'
PREFIX=''
;;
ibin)
SOURCE='/ibin /etc'
TYPE='ibin'
PREFIX=''
;;
db)
SOURCE="/backup/${SSERVER}/db/"
if [[ ! -d $SOURCE ]]; then
echo -e "${RED}Database backup directory $SOURCE does not exist!${CLEAR}"
exit 1
fi
TYPE='db'
PREFIX=''
;;
config)
SOURCE="/var/named /root /ibin /etc /var/spool/cron ${UNIQDNS}"
TYPE='configs'
PREFIX=''
;;
*)
echo -e "${RED}ERROR: Invalid Backup Type${CLEAR}"
exit 1
;;
esac
# Determine Backup Destination
echo -e "${LBLUE}Determining Backup Destination Paths${CLEAR}"
case $2 in
local)
echo -e "${NBLUE}Backing up: $SSERVER to: $LocServer${CLEAR}"
TPATH_CURRENT="${BACKUP_FOLDER}/${SSERVER}/$TYPE"
TPATH_BACKUP="${BACKUP_FOLDER}/${SSERVER}/$TYPE"
mkdir -p $TPATH_CURRENT || { echo -e "${RED}Failed to create directory $TPATH_CURRENT${CLEAR}"; exit 1; }
;;
external)
echo -e "${NBLUE}Backing up: $SSERVER to: $ExtSERVER${CLEAR}"
TPATH_CURRENT="${BACKUP_FOLDER}/${SSERVER}/$TYPE"
TPATH_BACKUP="$ExtSERVER:${BACKUP_FOLDER}/${SSERVER}/$TYPE"
ssh $ExtSERVER "mkdir -p $TPATH_CURRENT" || { echo -e "${RED}SSH command failed on $ExtSERVER${CLEAR}"; exit 1; }
;;
remote)
echo -e "${NBLUE}Backing up: $SSERVER to: $RemSERVER${CLEAR}"
TPATH_CURRENT="${BACKUP_FOLDER}/${SSERVER}/$TYPE"
TPATH_BACKUP="$RemSERVER:${BACKUP_FOLDER}/${SSERVER}/$TYPE"
ssh $RemSERVER "mkdir -p $TPATH_CURRENT" || { echo -e "${RED}SSH command failed on $RemSERVER${CLEAR}"; exit 1; }
;;
*)
echo -e "${RED}ERROR: No Backup Destination Specified${CLEAR}"
exit 1
;;
esac
{
echo "----------------------------------------------------------------------------------------------------------------------------------------------------"
echo "---- Server:$SSERVER Backup Type:$TYPE Dest:$2 -- Location:$TPATH_CURRENT/$PREFIX$date ------------------------------------"
echo "----------------------------------------------------------------------------------------------------------------------------------------------------"
} >> $BACKUP_LOG
if [[ $3 == "debug" ]]; then
echo -e "${LBLUE}Display Command Mode${CLEAR}"
echo -e "${NBLUE}Backing Up $SOURCE/${CLEAR}"
echo "Command: nice -n 19 rsync $OPTS --link-dest=$TPATH_CURRENT/current $SOURCE $TPATH_BACKUP/$PREFIX$date --exclude-from=$BACKUP_EXCLUDE --log-file=$BACKUP_LOG"
exit 0
fi
# OLD
###echo -e "${LBLUE}Executing Backup${CLEAR}"
###echo -e "${NBLUE}Backing Up $SOURCE/${CLEAR}"
###nice -n 19 rsync $OPTS --link-dest=$TPATH_CURRENT/current $SOURCE $TPATH_BACKUP/$PREFIX$date --exclude-from=$BACKUP_EXCLUDE --log-file=$BACKUP_LOG
###RSYNC_EXIT_CODE=$?
###if [[ $RSYNC_EXIT_CODE -ne 0 ]]; then
### subject="Backup Job on $SSERVER Failed"
### message="Backup Job on $SSERVER FAILED!\nRun: $readable_date\nBackup Type: $TYPE\nDestination: $2\nRsync exited with code $RSYNC_EXIT_CODE.\nCheck the backup log at $BACKUP_LOG for details."
### send_email "$subject" "$message"
### echo -e "${RED}Backup failed with rsync exit code $RSYNC_EXIT_CODE${CLEAR}"
### exit 1
###fi
# OLD END
echo -e "${LBLUE}Executing Backup${CLEAR}"
echo -e "${NBLUE}Backing Up $SOURCE/${CLEAR}"
# Ensure we capture rsync's real exit code even through the pipe
set -o pipefail
nice -n 19 rsync $OPTS --itemize-changes --progress \
--link-dest="$TPATH_CURRENT/current" \
$SOURCE "$TPATH_BACKUP/$PREFIX$date" \
--exclude-from="$BACKUP_EXCLUDE" \
--log-file="$BACKUP_LOG" \
| awk '
function color(c,s){ printf("%s%s\033[0m\n", c, s) }
/^\*deleting/ { color("\033[31m",$0); next } # red: deletions
/^>f\+\+\+\+\+\+\+/ { color("\033[32m",$0); next } # green: new files
/^>f\./ { color("\033[33m",$0); next } # yellow: updated files
/^cd\+\+\+\+\+\+\+/ { color("\033[36m",$0); next } # cyan: new directories
/^[>cshd][^ ]* / { color("\033[32m",$0); next } # green: other transfers
/^\.[^ ]* / { color("\033[90m",$0); next } # gray: metadata-only changes
/^[a-zA-Z0-9\/].*\/$/ { color("\033[36m",$0); next } # cyan: directory names
/^[a-zA-Z0-9].*[^\/]$/ { color("\033[37m",$0); next } # white: file names
{ print } # anything else (progress, etc.)
'
RSYNC_EXIT_CODE=${PIPESTATUS[0]}
# Cleanup Folder Structure
echo -e "${LBLUE}Cleanup Folder Structure Process${CLEAR}"
case $2 in
local)
rm -rf $TPATH_CURRENT/current
ln -s $TPATH_CURRENT/$PREFIX$date $TPATH_CURRENT/current
;;
external)
ssh $ExtSERVER "/bin/rm -rf $TPATH_CURRENT/current && ln -s $TPATH_CURRENT/$PREFIX$date $TPATH_CURRENT/current" || {
echo -e "${RED}Failed to update current symlink on $ExtSERVER${CLEAR}"
exit 1
}
;;
remote)
ssh $RemSERVER "/bin/rm -rf $TPATH_CURRENT/current && ln -s $TPATH_CURRENT/$PREFIX$date $TPATH_CURRENT/current" || {
echo -e "${RED}Failed to update current symlink on $RemSERVER${CLEAR}"
exit 1
}
;;
*)
echo -e "${RED}ERROR: No Backup Type Selected${CLEAR}"
exit 1
;;
esac
# Cleanup Old Backups
echo -e "${LBLUE}Deleting Older Backups${CLEAR}"
cleanup_command="/ibin/cleanup $TPATH_CURRENT"
if [[ $1 == "db" ]]; then
cleanup_command="/ibin/cleanup-files $TPATH_CURRENT"
fi
case $2 in
local)
eval $cleanup_command || { echo -e "${RED}Cleanup command failed locally${CLEAR}"; exit 1; }
;;
external)
ssh $ExtSERVER "$cleanup_command" || { echo -e "${RED}Cleanup command failed on $ExtSERVER${CLEAR}"; exit 1; }
;;
remote)
ssh $RemSERVER "$cleanup_command" || { echo -e "${RED}Cleanup command failed on $RemSERVER${CLEAR}"; exit 1; }
;;
*)
echo -e "${RED}ERROR: No Backup Destination Selected${CLEAR}"
exit 1
;;
esac
# Cleanup Old Log Files
echo -e "${LBLUE}Deleting Older Log Files${CLEAR}"
/ibin/cleanup-files /var/log/cpbackup/ || { echo -e "${RED}Failed to clean up old log files${CLEAR}"; exit 1; }
# Send success email notification
subject="Backup Job on $SSERVER Completed Successfully"
message="Backup Job on $SSERVER completed successfully.\nRun: $readable_date\nBackup Type: $TYPE\nDestination: $2\nLog File: $BACKUP_LOG"
send_email "$subject" "$message"
exit 0