#!/bin/bash #============================================================================ #title : TUBS #description : The Ultimate Backup Script (Linux) Version 3 #author : Janic Voser Xelon #date : 2022-04-08 #updated : 2022-07-04 #version : 3.0 #usage : chmod +x tubs.sh && ./tubs.sh #notes : #bash_version : #============================================================================ # Config ## Configfile #CONFIG_FILE=/etc/tubs/config # Any Config File CONFIG_FILE="$PWD/tubs.config" # Functions ## Colorful Information function blue(){ # The blue color is used for inforation echo -e '\e[34m'"$1"'\e[0m' } function cyan(){ # The cyan color is only used for help echo -e '\e[0;36m'"$1"'\e[0m' } function green(){ # The green color is used for successful steps echo -e '\e[32m'"$1"'\e[0m' } function magenta(){ # The magenta color is used for warnings & important Information echo -e '\e[35m'"$1"'\e[0m' } function red(){ # The red color is used for failed steps echo -e '\e[31m'"$1"'\e[0m' && exit 1 } ## Critical Error function critical_error { red "Critical error during: $1" } ## Load Config function load_config { if [ -e "$CONFIG_FILE" ] then blue "Loading Config" source "$CONFIG_FILE" || critical_error "Reading Configfile" else red "Configfile not Found" fi } ## Create destination Structure function create_dst { if [ "$SSH_REMOTE_DST" == "YES" ] then ssh -i "$SSH_REMOTE_KEY_DST" -p "$SSH_REMOTE_PORT_DST" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST" mkdir -p "$BACKUP_DST_FULL" || critical_error "Creating Remote Directory BACKUP_DST_FULL" ssh -i "$SSH_REMOTE_KEY_DST" -p "$SSH_REMOTE_PORT_DST" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST" mkdir -p "$BACKUP_DST_INC" || critical_error "Creating Remote Directory BACKUP_DST_INC" else mkdir -p "$BACKUP_DST_FULL" || critical_error "Creating Directory BACKUP_DST_FULL" mkdir -p "$BACKUP_DST_INC" || critical_error "Creating Directory BACKUP_DST_INC" fi } ## Check destination function check_dst { if [ "$SSH_REMOTE_DST" == "YES" ] then magenta "This script is currently not able to check if remote Destinations exist" if ssh -i "$SSH_REMOTE_KEY_DST" -p "$SSH_REMOTE_PORT_DST" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST" [ -e "$BACKUP_DST" ] then blue "Remote Backup destinantion existing" else red "Remote Backup destination not existing" fi ssh -i "$SSH_REMOTE_KEY_DST" -p "$SSH_REMOTE_PORT_DST" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST" mkdir -p "$BACKUP_DST_INC_PATH" || critical_error "Error while creating remote incremental backup-directory ($BACKUP_DST_INC_PATH)" ssh -i "$SSH_REMOTE_KEY_DST" -p "$SSH_REMOTE_PORT_DST" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST" mkdir -p "$BACKUP_DST_FULL_LOGS" || critical_error "Error while creating remote log directory ($BACKUP_DST_FULL_LOGS)" else if [ -e "$BACKUP_DST" ] then blue "Backup destinantion existing" else red "Backup destination not existing" fi mkdir -p "$BACKUP_DST_INC_PATH" || critical_error "Error while creating incremental backup-directory ($BACKUP_DST_INC_PATH)" mkdir -p "$BACKUP_DST_FULL_LOGS" || critical_error "Error while creating log directory ($BACKUP_DST_FULL_LOGS)" fi } ## Check sources local function check_src { for src_dir in "${BACKUP_SRC_ARRAY[@]}" do if [[ "$src_dir" =~ ^*[/] ]] then critical_error "$src_dir is ending with slash please remove it" fi if ! [ -d "$src_dir" ] then critical_error "$src_dir is not a directory" fi green "Source $src_dir is valid" done } ## Check Incremental Date function check_inc { if [ "$SSH_REMOTE_DST" == "YES" ] then check_inc_remote else check_inc_local fi BACKUP_DST_INC_PATH="$BACKUP_DST_INC/$BACKUP_PREFIX$LAST_BACKUP" } ## Check Incremental Date function check_inc_local { if ! [ -e "$BACKUP_LAST_FILE" ] then magenta "Missing last successfull backup file, just using the day before" LAST_BACKUP="$(date --date="yesterday" "$DATE_FORMAT")" else LAST_BACKUP="$(cat "$BACKUP_LAST_FILE")" if ! [[ $LAST_BACKUP =~ ^[0-9]{4}(-[0-9]{2}){2}_[0-9]{2}-[0-9]{2} ]] then magenta "Content of last successfull backup file was not valid" LAST_BACKUP="$(date --date="yesterday" "$DATE_FORMAT")" fi fi } ## Check Incremental Date function check_inc_remote { if ! ssh -i "$SSH_REMOTE_KEY_DST" -p "$SSH_REMOTE_PORT_DST" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST" [ -e "$BACKUP_LAST_FILE" ] then magenta "Missing last successfull backup file, just using the day before" LAST_BACKUP="$(date --date="yesterday" "$DATE_FORMAT")" else LAST_BACKUP="$(ssh -i "$SSH_REMOTE_KEY_DST" -p "$SSH_REMOTE_PORT_DST" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST" cat "$BACKUP_LAST_FILE")" if ! [[ $LAST_BACKUP =~ ^[0-9]{4}(-[0-9]{2}){2}_[0-9]{2}-[0-9]{2} ]] then magenta "Content of last successfull backup file was not valid" LAST_BACKUP="$(date --date="yesterday" "$DATE_FORMAT")" fi fi } ## Check Prerequirements function check_prereq { command -v rsync &> /dev/null || critical_error "Missing rsync" green "All requirements are saturated" } ## Last successful backup function last_backup { if [ "$SSH_REMOTE_DST" == "YES" ] then last_backup_remote else last_backup_local fi } ### Last successful backup function last_backup_local { if touch "$BACKUP_LAST_FILE" &> /dev/null then echo "$BACKUP_DATE" > "$BACKUP_LAST_FILE" else critical_error "Can not write Last successful backup file" fi } ### Last successful backup function last_backup_remote { if ssh -i "$SSH_REMOTE_KEY_DST" -p "$SSH_REMOTE_PORT_DST" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST" touch "$BACKUP_LAST_FILE" &> /dev/null then echo "$BACKUP_DATE" | ssh -i "$SSH_REMOTE_KEY_DST" -p "$SSH_REMOTE_PORT_DST" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST" -T "cat > $BACKUP_LAST_FILE" else critical_error "Can not write Last successful backup file" fi } ## Backup all function backup_all { if [ "$SSH_REMOTE_DST" == "YES" ] then backup_all_remote else backup_all_local fi } ## Backup all local function backup_all_local { blue "Starting Backup Processes" for src_dir in "${BACKUP_SRC_ARRAY[@]}" do src_dir_name="$(echo "$src_dir" | rev | cut -d / -f 1 | rev)" cat << EOF >> "$BACKUP_DST_FULL_LOGS/$src_dir_name.log" ################################################################################ #############################TUBS $BACKUP_DATE############################## ################################################################################ EOF rsync "$RSYNC_ARGS" --backup --delete --backup-dir="$BACKUP_DST_INC_PATH/" "$src_dir" "$BACKUP_DST_FULL" >> "$BACKUP_DST_FULL_LOGS/$src_dir_name.log" & done green "All backup processes started successfull" blue "Waiting for processes to finish" # wait for all pids jobs=$(jobs -p) for process in $jobs do wait "$process" || critical_error "Process: $process failed" done green "All Processes finished" } ## Backup all remote function backup_all_remote { blue "Starting Backup Processes" for src_dir in "${BACKUP_SRC_ARRAY[@]}" do src_dir_name="$(echo "$src_dir" | rev | cut -d / -f 1 | rev)" ssh -i "$SSH_REMOTE_KEY_DST" -p "$SSH_REMOTE_PORT_DST" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST" -T "cat << EOF >> \"$BACKUP_DST_FULL_LOGS/$src_dir_name.log\" ################################################################################ #############################TUBS $BACKUP_DATE############################## ################################################################################ EOF " rsync "$RSYNC_ARGS" -e "ssh -p $SSH_REMOTE_PORT_DST -i $SSH_REMOTE_KEY_DST" --backup --delete --backup-dir="$BACKUP_DST_INC_PATH/" "$src_dir" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST:$BACKUP_DST_FULL" | ssh -i "$SSH_REMOTE_KEY_DST" -p "$SSH_REMOTE_PORT_DST" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST" -T "cat >> \"$BACKUP_DST_FULL_LOGS/$src_dir_name.log\"" & done green "All backup processes started successfull" blue "Waiting for processes to finish" # wait for all pids jobs=$(ssh -i "$SSH_REMOTE_KEY_DST" -p "$SSH_REMOTE_PORT_DST" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST" jobs -p) for process in $jobs do ssh -i "$SSH_REMOTE_KEY_DST" -p "$SSH_REMOTE_PORT_DST" "$SSH_REMOTE_USER_DST@$SSH_REMOTE_HOST_DST" wait "$process" || critical_error "Process: $process failed" done green "All Processes finished" } ## Pre Backup Script function pre_backup_script { if [ -e "$PRE_BACKUP_SCRIPT" ] then if [ -x "$PRE_BACKUP_SCRIPT" ] then blue "Executing Pre-Backup-Script" $PRE_BACKUP_SCRIPT green "Pre-Backup-Script finished" else magenta "Pre-Backup-Script is not executable" fi else blue "No Pre-Backup-Script found" fi } ## Post Backup Script function post_backup_script { if [ -e "$POST_BACKUP_SCRIPT" ] then if [ -x "$POST_BACKUP_SCRIPT" ] then blue "Executing Post-Backup-Script" $POST_BACKUP_SCRIPT green "Post-Backup-Script finished" else magenta "Post-Backup-Script is not executable" fi else blue "No Pre-Backup-Script found" fi } ## Generate Variables load_config # Load config first (Variables are dependent on it) BACKUP_DATE="$(date "$DATE_FORMAT")" BACKUP_DST_FULL="$BACKUP_DST/FULL" BACKUP_DST_FULL_LOGS="$BACKUP_DST_FULL/logs" BACKUP_DST_INC="$BACKUP_DST/INCREMENTAL" BACKUP_LAST_FILE="$BACKUP_DST/LAST_SUCCESSFULL_BACKUP" # Main Script ## Load config if [ -n "$1" ] then case "$1" in help) cyan "Execute this script with one of the following arguments" cyan " - help --> Display help (your reading it right now)" cyan " - init --> Initialize the backup Structure" cyan " - backup --> Do the accual backup" cyan " - restore --> Restore all the data from a certain day" ;; init) blue "Starting initialisation" create_dst blue "Initialisation was successfull" ;; backup) green "TUBS started" check_inc check_dst check_src check_prereq last_backup pre_backup_script backup_all post_backup_script green "TUBS finished" ;; restore) magenta "I am kinda sorry this is not implemented yet" exit ;; *) red "Your supplied argument was not valid use help if you don't know what you are doing" ;; esac exit else cyan "Please supply a valid argument or execute with help flag" fi