commit 303c790d99197c2460c6467dbc79c6e034bb8ea0 Author: Patrick Niebeling Date: Tue Nov 19 11:42:07 2024 +0100 First Commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fccb523 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Jonas Friedmann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7fc311c --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ +tar-multibackup +=============== +Bash script to backup multiple folders and to clean up old backups based on a retention time. Features configurable post/pre-commands, tar excludes as well as backup retentions. + +### Installation + cd /usr/local/src + git clone https://github.com/frdmn/tar-multibackup.git + ln -sf /usr/local/src/tar-multibackup/multibackup /usr/local/bin/multibackup + cp /usr/local/src/tar-multibackup/multibackup.conf ~/.multibackup.conf + +### Configuration and usage + +* `timestamp` = Format of the timestamp, used in the backup target filename +* `backup_destination` = Directory which is used to store the archives/backups +* `folders_to_backup` = Array of folders to backup +* `backup_retention` = Retention time how long we should keep the backups +* `pre_commands` = Array of commands that are executed before the backup starts (stop specific service) +* `post_commands` = Array of commands that are executed after the backup finished (start specific service) +* `tar_options` = Additional tar Options + +### Environment configurations + +* `DEBUG` = if set to "true", `set -x` will be set +* `CONFIG` = if you want to use a different configuration file than the + +Example: + + CONFIG=/tmp/testbackup.conf DEBUG=true multibackup + +#### Example configuration + +In the example below you can find a `multibackup` configuration file to backup a productional [LiveConfig](http://www.liveconfig.com/) instance. + +`vi ~/.multibackup.conf` + + # Timestamp format, used in the backup target filename + timestamp=$(date +%Y%m%d) + + # Destination where you want to store your backups + backup_destination="/var/backups" + + # Folders to backup + folders_to_backup=( + "/etc" + "/var/mail" + "/var/www" + "/var/lib/mysql" + "/var/spool/cron" + "/var/lib/liveconfig" + ) + + # Files and folders that are excluded in the tar command + tar_excludes=( + "nginx-php-fcgi.sock" + ) + + # Additional tar Options + tar_options=() + + # How long to you want to keep your backups (in days) + backup_retention="+7" + + # Commands that are executed before the backup started + # (We have to make sure the liveconfig process is not running) + # (otherwise the databases changes while we try to save it) + pre_commands=( + "service liveconfig stop" + ) + + # Commands that are executed after the backup is completed + # (To restart the liveconfig process again, once the backup is completed) + post_commands=( + "service liveconfig start" + ) + +#### Cronjob setup + +To make sure the backup is executed automatically and recurring, we're going to add a simple cronjob: + +`vi /etc/cron.d/backup-liveconfig` + + # + # cronjob to backup LiveConfig, daily at 5:00 am + # + + 0 5 * * * root /usr/local/bin/multibackup &>/dev/null + +#### CREDITS +This Backup Script is a fork of https://github.com/frdmn/tar-multibackup \ No newline at end of file diff --git a/multibackup b/multibackup new file mode 100644 index 0000000..f007871 --- /dev/null +++ b/multibackup @@ -0,0 +1,178 @@ +#!/usr/bin/env bash + +[[ -n "${DEBUG}" ]] && set -x + +### +# Configuration +### + +# Use "~/.multibackup" as default config, if $CONFIG is not specified +CONFIG="${CONFIG:-${HOME}/.multibackup.conf}" + +### +# Functions +### + +# Color helpers +color_red=$(tput setaf 1) +color_yellow=$(tput setaf 3) +color_green=$(tput setaf 2) +color_reset=$(tput sgr0) + +# Logging prefix helper +prefix(){ + # Check if placeholders variables are given + if [[ -n "${current_action}" && -n "${current_task}" && -n "${total_tasks}" ]]; then + # Return prefix + echo "[${current_action}] [${current_task}/${total_tasks}] " + fi +} + +# Logging helpers +debug() { + echo "$(prefix)[DEBUG] $@ " 1>&2 +} + +success() { + echo "$(prefix)${color_green}[SUCCESS]${color_reset} $@" 1>&2 +} + +info() { + echo "$(prefix)${color_yellow}[INFO]${color_reset} $@" 1>&2 +} + +error() { + echo "$(prefix)${color_red}[ERROR]${color_reset} $@" 1>&2 +} + +# Strip all redundant slashes in file paths +strip_duplicate_slashes_in_path(){ + # Backup current shopt options + shoptBackup=$(shopt -p) + # Set extented globbing + shopt -s extglob + # Substitute input (turn multiple slashes into single one) and return + echo "${@//+(\/)//}" + # Restore shopt + eval "$oldShoptOptions" &> /dev/null +} + +# Replace all slashes with dashes +replace_slash_with_dash(){ + input="${@////-}" # replace all slashes with dashes + input="${input/#-/}" # remove possible starting dash + output="${input/%-/}" # as well as possible trailing dash + echo "${output}" +} + +### +# Logic +### + +# Check for config file then read and source +if [[ -f "${CONFIG}" ]]; then + . "${CONFIG}" +else + error "Couldn't find configuration file \"${CONFIG}\"" + exit 1 +fi + +# Iterate through tar_excludes to create a "--exclude=XXX" combination string +tar_exclude_parameters=() +if [[ "${#tar_excludes[@]}" -ne 0 ]]; then + # Run each pre command + for parameter in "${tar_excludes[@]}"; do + tar_exclude_parameters+=( "--exclude=${parameter}" ) + done +fi + +# Set prefix variables for pre-commands +current_action="pre-command" +current_task=1 +total_tasks="${#pre_commands[@]}" + +# Iterate through pre_commnads +if [[ "${#pre_commands[@]}" -ne 0 ]]; then + info "Found pre commands..." + # Run each pre command + for pre_command in "${pre_commands[@]}"; do + info "Running \"${pre_command}\":" + eval "${pre_command}" + # Check if pre command ran successfully + if [[ $? -eq 0 ]]; then + success "Pre command \"${pre_command}\" successfully completed!" + else + error "Pre command \"${pre_command}\" failed..." + fi + done +fi + +# Set prefix variables for backup +current_action="backup" +current_task=1 +total_tasks="${#folders_to_backup[@]}" + +# Iterate through "$folders_to_backup" +for folder_to_backup in "${folders_to_backup[@]}"; do + # Check if folder exists + debug "Check if source folder \"${folder_to_backup}\" exists..." + if [[ -d "${folder_to_backup}" ]]; then + # Exist => continue + info "Source folder \"${folder_to_backup}\" exists!" + # Make sure backup destination exists + backup_basename=$(replace_slash_with_dash "${folder_to_backup}") + absolute_backup_destination=$(strip_duplicate_slashes_in_path "${backup_destination}/${backup_basename}") + if [[ ! -d "${absolute_backup_destination}" ]]; then + info "Backup destination folder \"${absolute_backup_destination}\" doesn't exist. Creating..." + mkdir -p "${absolute_backup_destination}" + fi + + # Check if backup already exists (to make sure) + if [[ -f "${absolute_backup_destination}/${timestamp}.tar.gz" ]]; then + error "Backup \"${absolute_backup_destination}/${timestamp}.tar.gz\" already exists. Skipping..." + else + # Start backup + info "Starting backup \"${absolute_backup_destination}/${timestamp}.tar.gz\"" + tar ${tar_options} -czf "${absolute_backup_destination}/${timestamp}.tar.gz" -P "${folder_to_backup}" "${tar_exclude_parameters[@]}" + if [[ $? -eq 0 ]]; then + success "Backup \"${absolute_backup_destination}/${timestamp}.tar.gz\" successfully completed!" + else + error "Backup \"${absolute_backup_destination}/${timestamp}.tar.gz\" failed..." + fi + fi + + # Remove old backups + if [[ ! -z "${backup_retention}" ]]; then + find "${absolute_backup_destination}/" -maxdepth 1 -mtime "${backup_retention}" -type f -exec rm -rf {} \; + fi + else + # Doesn't exist => skip + error "Folder \"${folder_to_backup}\" doesn't exist. Skipping..." + fi + # Increment $current_task variable + current_task=$((current_task+1)) +done + +# Set prefix variables for pre-commands +current_action="post-command" +current_task=1 +total_tasks="${#post_commands[@]}" + +# Iterate through post_commnads +if [[ "${#post_commands[@]}" -ne 0 ]]; then + info "Found post commands..." + # Run each post command + for post_command in "${post_commands[@]}"; do + info "Running \"${post_command}\":" + eval "${post_command}" + # Check if post command ran successfully + if [[ $? -eq 0 ]]; then + success "Post command \"${post_command}\" successfully completed!" + else + error "Post command \"${post_command}\" failed..." + fi + done +fi + +# Return exit code +exit 0 diff --git a/multibackup.conf b/multibackup.conf new file mode 100644 index 0000000..57bab9d --- /dev/null +++ b/multibackup.conf @@ -0,0 +1,26 @@ +# Timestamp format, used in the backup target filename +timestamp=$(date +%Y%m%d) + +# Destination where you want to store your backups +backup_destination="/var/backups" + +# Folders to backup +folders_to_backup=( + "/var/www" + "/var/lib/mysql" +) + +# Files and folders that are excluded in the tar command +tar_excludes=() + +# Additional tar Options +tar_options=() + +# How long to you want to keep your backups (in days) +backup_retention="+7" + +# Commands that are executed before the backup started +pre_commands=() + +# Commands that are executed after the backup is completed +post_commands=()