1
0
mirror of https://github.com/deajan/obackup.git synced 2024-12-25 23:13:41 +01:00

Rebuilt targets

This commit is contained in:
deajan 2016-08-31 11:26:21 +02:00
parent 7ccc469dd6
commit bb8cf4a27c
3 changed files with 453 additions and 314 deletions

View File

@ -5,15 +5,19 @@ PROGRAM="obackup"
AUTHOR="(C) 2013-2016 by Orsiris de Jong" AUTHOR="(C) 2013-2016 by Orsiris de Jong"
CONTACT="http://www.netpower.fr/obackup - ozy@netpower.fr" CONTACT="http://www.netpower.fr/obackup - ozy@netpower.fr"
PROGRAM_VERSION=2.1-dev PROGRAM_VERSION=2.1-dev
PROGRAM_BUILD=2016082603 PROGRAM_BUILD=2016083002
IS_STABLE=no IS_STABLE=yes
#### MINIMAL-FUNCTION-SET BEGIN #### #### MINIMAL-FUNCTION-SET BEGIN ####
## FUNC_BUILD=2016082605 ## FUNC_BUILD=2016083003
## BEGIN Generic functions for osync & obackup written in 2013-2016 by Orsiris de Jong - http://www.netpower.fr - ozy@netpower.fr ## BEGIN Generic bash functions written in 2013-2016 by Orsiris de Jong - http://www.netpower.fr - ozy@netpower.fr
## To use in a program, define the following variables:
## PROGRAM=program-name
## INSTANCE_ID=program-instance-name
## _DEBUG=yes/no
## type -p does not work on platforms other than linux (bash). If if does not work, always assume output is not a zero exitcode
if ! type "$BASH" > /dev/null; then if ! type "$BASH" > /dev/null; then
echo "Please run this script only with bash shell. Tested on bash >= 3.2" echo "Please run this script only with bash shell. Tested on bash >= 3.2"
exit 127 exit 127
@ -26,20 +30,21 @@ export LC_ALL=C
MAIL_ALERT_MSG="Execution of $PROGRAM instance $INSTANCE_ID on $(date) has warnings/errors." MAIL_ALERT_MSG="Execution of $PROGRAM instance $INSTANCE_ID on $(date) has warnings/errors."
# Environment variables that can be overriden by programs # Environment variables that can be overriden by programs
_DRYRUN=0 _DRYRUN=false
_SILENT=0 _SILENT=false
_VERBOSE=false
_LOGGER_PREFIX="date" _LOGGER_PREFIX="date"
_LOGGER_STDERR=0 _LOGGER_STDERR=false
if [ "$KEEP_LOGGING" == "" ]; then if [ "$KEEP_LOGGING" == "" ]; then
KEEP_LOGGING=1801 KEEP_LOGGING=1801
fi fi
# Initial error status, logging 'WARN', 'ERROR' or 'CRITICAL' will enable alerts flags # Initial error status, logging 'WARN', 'ERROR' or 'CRITICAL' will enable alerts flags
ERROR_ALERT=0 ERROR_ALERT=false
WARN_ALERT=0 WARN_ALERT=false
# Current log # Log from current run
CURRENT_LOG CURRENT_LOG=""
## allow function call checks #__WITH_PARANOIA_DEBUG ## allow function call checks #__WITH_PARANOIA_DEBUG
if [ "$_PARANOIA_DEBUG" == "yes" ];then #__WITH_PARANOIA_DEBUG if [ "$_PARANOIA_DEBUG" == "yes" ];then #__WITH_PARANOIA_DEBUG
@ -50,11 +55,11 @@ fi #__WITH_PARANOIA_DEBUG
if [ ! "$_DEBUG" == "yes" ]; then if [ ! "$_DEBUG" == "yes" ]; then
_DEBUG=no _DEBUG=no
SLEEP_TIME=.05 # Tested under linux and FreeBSD bash, #TODO tests on cygwin / msys SLEEP_TIME=.05 # Tested under linux and FreeBSD bash, #TODO tests on cygwin / msys
_VERBOSE=0 _VERBOSE=false
else else
SLEEP_TIME=1 SLEEP_TIME=1
trap 'TrapError ${LINENO} $?' ERR trap 'TrapError ${LINENO} $?' ERR
_VERBOSE=1 _VERBOSE=true
fi fi
SCRIPT_PID=$$ SCRIPT_PID=$$
@ -62,6 +67,10 @@ SCRIPT_PID=$$
LOCAL_USER=$(whoami) LOCAL_USER=$(whoami)
LOCAL_HOST=$(hostname) LOCAL_HOST=$(hostname)
if [ "$PROGRAM" == "" ]; then
PROGRAM="ofunctions"
fi
## Default log file until config file is loaded ## Default log file until config file is loaded
if [ -w /var/log ]; then if [ -w /var/log ]; then
LOG_FILE="/var/log/$PROGRAM.log" LOG_FILE="/var/log/$PROGRAM.log"
@ -104,17 +113,21 @@ function _Logger {
echo -e "$lvalue" >> "$LOG_FILE" echo -e "$lvalue" >> "$LOG_FILE"
CURRENT_LOG="$CURRENT_LOG"$'\n'"$lvalue" CURRENT_LOG="$CURRENT_LOG"$'\n'"$lvalue"
if [ "$_LOGGER_STDERR" -eq 1 ]; then if [ $_LOGGER_STDERR == true ]; then
cat <<< "$evalue" 1>&2 cat <<< "$evalue" 1>&2
elif [ "$_SILENT" -eq 0 ]; then elif [ "$_SILENT" == false ]; then
echo -e "$svalue" echo -e "$svalue"
fi fi
} }
# General log function with log levels # General log function with log levels:
# CRITICAL, ERROR, WARN are colored in stdout, prefixed in stderr
# NOTICE is standard level
# VERBOSE is only sent to stdout / stderr if _VERBOSE=true
# DEBUG & PARANOIA_DEBUG are only sent if _DEBUG=yes
function Logger { function Logger {
local value="${1}" # Sentence to log (in double quotes) local value="${1}" # Sentence to log (in double quotes)
local level="${2}" # Log level: PARANOIA_DEBUG, DEBUG, NOTICE, WARN, ERROR, CRITIAL local level="${2}" # Log level: PARANOIA_DEBUG, DEBUG, VERBOSE, NOTICE, WARN, ERROR, CRITIAL
if [ "$_LOGGER_PREFIX" == "time" ]; then if [ "$_LOGGER_PREFIX" == "time" ]; then
prefix="TIME: $SECONDS - " prefix="TIME: $SECONDS - "
@ -126,19 +139,24 @@ function Logger {
if [ "$level" == "CRITICAL" ]; then if [ "$level" == "CRITICAL" ]; then
_Logger "$prefix\e[41m$value\e[0m" "$prefix$level:$value" "$level:$value" _Logger "$prefix\e[41m$value\e[0m" "$prefix$level:$value" "$level:$value"
ERROR_ALERT=1 ERROR_ALERT=true
return return
elif [ "$level" == "ERROR" ]; then elif [ "$level" == "ERROR" ]; then
_Logger "$prefix\e[91m$value\e[0m" "$prefix$level:$value" "$level:$value" _Logger "$prefix\e[91m$value\e[0m" "$prefix$level:$value" "$level:$value"
ERROR_ALERT=1 ERROR_ALERT=true
return return
elif [ "$level" == "WARN" ]; then elif [ "$level" == "WARN" ]; then
_Logger "$prefix\e[93m$value\e[0m" "$prefix$level:$value" "$level:$value" _Logger "$prefix\e[93m$value\e[0m" "$prefix$level:$value" "$level:$value"
WARN_ALERT=1 WARN_ALERT=true
return return
elif [ "$level" == "NOTICE" ]; then elif [ "$level" == "NOTICE" ]; then
_Logger "$prefix$value" _Logger "$prefix$value"
return return
elif [ "$level" == "VERBOSE" ]; then
if [ $_VERBOSE == true ]; then
_Logger "$prefix$value"
fi
return
elif [ "$level" == "DEBUG" ]; then elif [ "$level" == "DEBUG" ]; then
if [ "$_DEBUG" == "yes" ]; then if [ "$_DEBUG" == "yes" ]; then
_Logger "$prefix$value" _Logger "$prefix$value"
@ -150,7 +168,7 @@ function Logger {
return #__WITH_PARANOIA_DEBUG return #__WITH_PARANOIA_DEBUG
fi #__WITH_PARANOIA_DEBUG fi #__WITH_PARANOIA_DEBUG
else else
_Logger "\e[41mLogger function called without proper loglevel.\e[0m" _Logger "\e[41mLogger function called without proper loglevel [$level].\e[0m"
_Logger "$prefix$value" _Logger "$prefix$value"
fi fi
} }
@ -175,7 +193,7 @@ function QuickLogger {
__CheckArguments 1 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 1 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
if [ "$_SILENT" -eq 1 ]; then if [ $_SILENT == true ]; then
_QuickLogger "$value" "log" _QuickLogger "$value" "log"
else else
_QuickLogger "$value" "stdout" _QuickLogger "$value" "stdout"
@ -272,9 +290,9 @@ function SendAlert {
fi fi
body="$MAIL_ALERT_MSG"$'\n\n'"$CURRENT_LOG" body="$MAIL_ALERT_MSG"$'\n\n'"$CURRENT_LOG"
if [ $ERROR_ALERT -eq 1 ]; then if [ $ERROR_ALERT == true ]; then
subject="Error alert for $INSTANCE_ID" subject="Error alert for $INSTANCE_ID"
elif [ $WARN_ALERT -eq 1 ]; then elif [ $WARN_ALERT == true ]; then
subject="Warning alert for $INSTANCE_ID" subject="Warning alert for $INSTANCE_ID"
else else
subject="Alert for $INSTANCE_ID" subject="Alert for $INSTANCE_ID"
@ -526,7 +544,7 @@ function TrapError {
local job="$0" local job="$0"
local line="$1" local line="$1"
local code="${2:-1}" local code="${2:-1}"
if [ $_SILENT -eq 0 ]; then if [ $_SILENT == false ]; then
echo -e " /!\ ERROR in ${job}: Near line ${line}, exit code ${code}" echo -e " /!\ ERROR in ${job}: Near line ${line}, exit code ${code}"
fi fi
} }
@ -552,7 +570,7 @@ function LoadConfigFile {
} }
function Spinner { function Spinner {
if [ $_SILENT -eq 1 ]; then if [ $_SILENT == true ]; then
return 0 return 0
fi fi
@ -593,6 +611,7 @@ function joinString {
# Time control function for background processes, suitable for multiple synchronous processes # Time control function for background processes, suitable for multiple synchronous processes
# Fills a global variable called WAIT_FOR_TASK_COMPLETION that contains list of failed pids in format pid1:result1;pid2:result2 # Fills a global variable called WAIT_FOR_TASK_COMPLETION that contains list of failed pids in format pid1:result1;pid2:result2
# Warning: Don't imbricate this function into another run if you plan to use the global variable output # Warning: Don't imbricate this function into another run if you plan to use the global variable output
function WaitForTaskCompletion { function WaitForTaskCompletion {
local pids="${1}" # pids to wait for, separated by semi-colon local pids="${1}" # pids to wait for, separated by semi-colon
local soft_max_time="${2}" # If program with pid $pid takes longer than $soft_max_time seconds, will log a warning, unless $soft_max_time equals 0. local soft_max_time="${2}" # If program with pid $pid takes longer than $soft_max_time seconds, will log a warning, unless $soft_max_time equals 0.
@ -604,7 +623,7 @@ function WaitForTaskCompletion {
Logger "${FUNCNAME[0]} called by [$caller_name]." "PARANOIA_DEBUG" #__WITH_PARANOIA_DEBUG Logger "${FUNCNAME[0]} called by [$caller_name]." "PARANOIA_DEBUG" #__WITH_PARANOIA_DEBUG
__CheckArguments 6 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 6 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
local soft_alert=0 # Does a soft alert need to be triggered, if yes, send an alert once local soft_alert=false # Does a soft alert need to be triggered, if yes, send an alert once
local log_ttime=0 # local time instance for comparaison local log_ttime=0 # local time instance for comparaison
local seconds_begin=$SECONDS # Seconds since the beginning of the script local seconds_begin=$SECONDS # Seconds since the beginning of the script
@ -613,9 +632,15 @@ function WaitForTaskCompletion {
local retval=0 # return value of monitored pid process local retval=0 # return value of monitored pid process
local errorcount=0 # Number of pids that finished with errors local errorcount=0 # Number of pids that finished with errors
local pid # Current pid working on
local pidCount # number of given pids local pidCount # number of given pids
local pidState # State of the process local pidState # State of the process
local pidsArray # Array of currently running pids
local newPidsArray # New array of currently running pids
local hasPids=false # Are any valable pids given to function ? #__WITH_PARANOIA_DEBUG
IFS=';' read -a pidsArray <<< "$pids" IFS=';' read -a pidsArray <<< "$pids"
pidCount=${#pidsArray[@]} pidCount=${#pidsArray[@]}
@ -641,9 +666,9 @@ function WaitForTaskCompletion {
fi fi
if [ $exec_time -gt $soft_max_time ]; then if [ $exec_time -gt $soft_max_time ]; then
if [ $soft_alert -eq 0 ] && [ $soft_max_time -ne 0 ]; then if [ $soft_alert == true ] && [ $soft_max_time -ne 0 ]; then
Logger "Max soft execution time exceeded for task [$caller_name] with pids [$(joinString , ${pidsArray[@]})]." "WARN" Logger "Max soft execution time exceeded for task [$caller_name] with pids [$(joinString , ${pidsArray[@]})]." "WARN"
soft_alert=1 soft_alert=true
SendAlert true SendAlert true
fi fi
@ -663,6 +688,7 @@ function WaitForTaskCompletion {
fi fi
for pid in "${pidsArray[@]}"; do for pid in "${pidsArray[@]}"; do
if [ $(IsNumeric $pid) -eq 1 ]; then
if kill -0 $pid > /dev/null 2>&1; then if kill -0 $pid > /dev/null 2>&1; then
# Handle uninterruptible sleep state or zombies by ommiting them from running process array (How to kill that is already dead ? :) # Handle uninterruptible sleep state or zombies by ommiting them from running process array (How to kill that is already dead ? :)
#TODO(high): have this tested on *BSD, Mac & Win #TODO(high): have this tested on *BSD, Mac & Win
@ -676,17 +702,24 @@ function WaitForTaskCompletion {
retval=$? retval=$?
if [ $retval -ne 0 ]; then if [ $retval -ne 0 ]; then
errorcount=$((errorcount+1)) errorcount=$((errorcount+1))
Logger "${FUNCNAME[0]} called by [$caller_name] finished monitoring [$pid] with exitcode [$result]." "DEBUG" Logger "${FUNCNAME[0]} called by [$caller_name] finished monitoring [$pid] with exitcode [$retval]." "DEBUG"
if [ "$WAIT_FOR_TASK_COMPLETION" == "" ]; then if [ "$WAIT_FOR_TASK_COMPLETION" == "" ]; then
WAIT_FOR_TASK_COMPLETION="$pid:$result" WAIT_FOR_TASK_COMPLETION="$pid:$retval"
else else
WAIT_FOR_TASK_COMPLETION=";$pid:$result" WAIT_FOR_TASK_COMPLETION=";$pid:$retval"
fi fi
fi fi
fi fi
hasPids=true ##__WITH_PARANOIA_DEBUG
fi
done done
if [ $hasPids == false ]; then ##__WITH_PARANOIA_DEBUG
Logger "No valable pids given." "ERROR" ##__WITH_PARANOIA_DEBUG
fi ##__WITH_PARANOIA_DEBUG
pidsArray=("${newPidsArray[@]}") pidsArray=("${newPidsArray[@]}")
# Trivial wait time for bash to not eat up all CPU
sleep $SLEEP_TIME sleep $SLEEP_TIME
done done
@ -700,6 +733,75 @@ function WaitForTaskCompletion {
fi fi
} }
# Take a list of commands to run, runs them sequentially with numberOfProcesses commands simultaneously runs
# Returns the number of non zero exit codes from commands
function ParallelExec {
local numberOfProcesses="${1}" # Number of simultaneous commands to run
local commandsArg="${2}" # Semi-colon separated list of commands
local pid
local runningPids=0
local counter=0
local commandsArray
local pidsArray
local newPidsArray
local retval
local retvalAll=0
local pidState
local commandsArrayPid
local hasPids=false # Are any valable pids given to function ? #__WITH_PARANOIA_DEBUG
IFS=';' read -r -a commandsArray <<< "$commandsArg"
Logger "Runnning ${#commandsArray[@]} commands in $numberOfProcesses simultaneous processes." "DEBUG"
while [ $counter -lt "${#commandsArray[@]}" ] || [ ${#pidsArray[@]} -gt 0 ]; do
while [ $counter -lt "${#commandsArray[@]}" ] && [ ${#pidsArray[@]} -lt $numberOfProcesses ]; do
Logger "Running command [${commandsArray[$counter]}]." "DEBUG"
eval "${commandsArray[$counter]}" &
pid=$!
pidsArray+=($pid)
commandsArrayPid[$pid]="${commandsArray[$counter]}"
counter=$((counter+1))
done
newPidsArray=()
for pid in "${pidsArray[@]}"; do
if [ $(IsNumeric $pid) -eq 1 ]; then
# Handle uninterruptible sleep state or zombies by ommiting them from running process array (How to kill that is already dead ? :)
if kill -0 $pid > /dev/null 2>&1; then
pidState=$(ps -p$pid -o state= 2 > /dev/null)
if [ "$pidState" != "D" ] && [ "$pidState" != "Z" ]; then
newPidsArray+=($pid)
fi
else
# pid is dead, get it's exit code from wait command
wait $pid
retval=$?
if [ $retval -ne 0 ]; then
Logger "Command [${commandsArrayPid[$pid]}] failed with exit code [$retval]." "ERROR"
retvalAll=$((retvalAll+1))
fi
fi
hasPids=true ##__WITH_PARANOIA_DEBUG
fi
done
if [ $hasPids == false ]; then ##__WITH_PARANOIA_DEBUG
Logger "No valable pids given." "ERROR" ##__WITH_PARANOIA_DEBUG
fi ##__WITH_PARANOIA_DEBUG
pidsArray=("${newPidsArray[@]}")
# Trivial wait time for bash to not eat up all CPU
sleep $SLEEP_TIME
done
return $retvalAll
}
function CleanUp { function CleanUp {
__CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
@ -887,7 +989,7 @@ function RunLocalCommand {
local hard_max_time="${2}" # Max time to wait for command to compleet local hard_max_time="${2}" # Max time to wait for command to compleet
__CheckArguments 2 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 2 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
if [ $_DRYRUN -ne 0 ]; then if [ $_DRYRUN == true ]; then
Logger "Dryrun: Local command [$command] not run." "NOTICE" Logger "Dryrun: Local command [$command] not run." "NOTICE"
return 0 return 0
fi fi
@ -902,7 +1004,7 @@ function RunLocalCommand {
Logger "Command failed." "ERROR" Logger "Command failed." "ERROR"
fi fi
if [ $_VERBOSE -eq 1 ] || [ $retval -ne 0 ]; then if [ $_VERBOSE == true ] || [ $retval -ne 0 ]; then
Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "NOTICE" Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "NOTICE"
fi fi
@ -920,7 +1022,7 @@ function RunRemoteCommand {
CheckConnectivity3rdPartyHosts CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost CheckConnectivityRemoteHost
if [ $_DRYRUN -ne 0 ]; then if [ $_DRYRUN == true ]; then
Logger "Dryrun: Local command [$command] not run." "NOTICE" Logger "Dryrun: Local command [$command] not run." "NOTICE"
return 0 return 0
fi fi
@ -937,7 +1039,7 @@ function RunRemoteCommand {
Logger "Command failed." "ERROR" Logger "Command failed." "ERROR"
fi fi
if [ -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ] && ([ $_VERBOSE -eq 1 ] || [ $retval -ne 0 ]) if [ -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ] && ([ $_VERBOSE == true ] || [ $retval -ne 0 ])
then then
Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "NOTICE" Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "NOTICE"
fi fi
@ -1011,7 +1113,7 @@ function CheckConnectivity3rdPartyHosts {
if [ "$_PARANOIA_DEBUG" != "yes" ]; then # Do not loose time in paranoia debug if [ "$_PARANOIA_DEBUG" != "yes" ]; then # Do not loose time in paranoia debug
if [ "$REMOTE_3RD_PARTY_HOSTS" != "" ]; then if [ "$REMOTE_3RD_PARTY_HOSTS" != "" ]; then
remote_3rd_party_success=0 remote_3rd_party_success=false
for i in $REMOTE_3RD_PARTY_HOSTS for i in $REMOTE_3RD_PARTY_HOSTS
do do
eval "$PING_CMD $i > /dev/null 2>&1" & eval "$PING_CMD $i > /dev/null 2>&1" &
@ -1019,11 +1121,11 @@ function CheckConnectivity3rdPartyHosts {
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot ping 3rd party host $i" "NOTICE" Logger "Cannot ping 3rd party host $i" "NOTICE"
else else
remote_3rd_party_success=1 remote_3rd_party_success=true
fi fi
done done
if [ $remote_3rd_party_success -ne 1 ]; then if [ $remote_3rd_party_success == false ]; then
Logger "No remote 3rd party host responded to ping. No internet ?" "ERROR" Logger "No remote 3rd party host responded to ping. No internet ?" "ERROR"
return 1 return 1
else else
@ -1054,14 +1156,14 @@ function __CheckArguments {
# In order to avoid this, we need to iterate over ${4} and count # In order to avoid this, we need to iterate over ${4} and count
local iterate=4 local iterate=4
local fetchArguments=1 local fetchArguments=true
local argList="" local argList=""
local countedArguments local countedArguments
while [ $fetchArguments -eq 1 ]; do while [ $fetchArguments == true ]; do
cmd='argument=${'$iterate'}' cmd='argument=${'$iterate'}'
eval $cmd eval $cmd
if [ "$argument" = "" ]; then if [ "$argument" = "" ]; then
fetchArguments=0 fetchArguments=false
else else
argList="$arg_list [Argument $(($iterate-3)): $argument]" argList="$arg_list [Argument $(($iterate-3)): $argument]"
iterate=$(($iterate+1)) iterate=$(($iterate+1))
@ -1203,7 +1305,7 @@ function PreInit {
## Set rsync default arguments ## Set rsync default arguments
RSYNC_ARGS="-rltD" RSYNC_ARGS="-rltD"
if [ "$_DRYRUN" -eq 1 ]; then if [ "$_DRYRUN" == true ]; then
RSYNC_DRY_ARG="-n" RSYNC_DRY_ARG="-n"
DRY_WARNING="/!\ DRY RUN" DRY_WARNING="/!\ DRY RUN"
else else
@ -1369,8 +1471,8 @@ PARTIAL_DIR=".obackup_workdir_partial"
# $FILE_SIZE_LIST_LOCAL, list of all directories to include in GetDirectoriesSize, enclosed by escaped doublequotes for local command # $FILE_SIZE_LIST_LOCAL, list of all directories to include in GetDirectoriesSize, enclosed by escaped doublequotes for local command
# $FILE_SIZE_LIST_REMOTE, list of all directories to include in GetDirectoriesSize, enclosed by escaped singlequotes for remote command # $FILE_SIZE_LIST_REMOTE, list of all directories to include in GetDirectoriesSize, enclosed by escaped singlequotes for remote command
CAN_BACKUP_SQL=1 CAN_BACKUP_SQL=true
CAN_BACKUP_FILES=1 CAN_BACKUP_FILES=true
function TrapStop { function TrapStop {
Logger "/!\ Manual exit of backup script. Backups may be in inconsistent state." "WARN" Logger "/!\ Manual exit of backup script. Backups may be in inconsistent state." "WARN"
@ -1380,7 +1482,7 @@ function TrapStop {
function TrapQuit { function TrapQuit {
local exitcode local exitcode
if [ $ERROR_ALERT -ne 0 ]; then if [ $ERROR_ALERT == true ]; then
if [ "$RUN_AFTER_CMD_ON_ERROR" == "yes" ]; then if [ "$RUN_AFTER_CMD_ON_ERROR" == "yes" ]; then
RunAfterHook RunAfterHook
fi fi
@ -1388,7 +1490,7 @@ function TrapQuit {
Logger "Backup script finished with errors." "ERROR" Logger "Backup script finished with errors." "ERROR"
SendAlert SendAlert
exitcode=1 exitcode=1
elif [ $WARN_ALERT -ne 0 ]; then elif [ $WARN_ALERT == true ]; then
if [ "$RUN_AFTER_CMD_ON_ERROR" == "yes" ]; then if [ "$RUN_AFTER_CMD_ON_ERROR" == "yes" ]; then
RunAfterHook RunAfterHook
fi fi
@ -1423,11 +1525,11 @@ function CheckEnvironment {
if [ "$SQL_BACKUP" != "no" ]; then if [ "$SQL_BACKUP" != "no" ]; then
if ! type mysqldump > /dev/null 2>&1 ; then if ! type mysqldump > /dev/null 2>&1 ; then
Logger "mysqldump not present. Cannot backup SQL." "CRITICAL" Logger "mysqldump not present. Cannot backup SQL." "CRITICAL"
CAN_BACKUP_SQL=0 CAN_BACKUP_SQL=false
fi fi
if ! type mysql > /dev/null 2>&1 ; then if ! type mysql > /dev/null 2>&1 ; then
Logger "mysql not present. Cannot backup SQL." "CRITICAL" Logger "mysql not present. Cannot backup SQL." "CRITICAL"
CAN_BACKUP_SQL=0 CAN_BACKUP_SQL=false
fi fi
fi fi
fi fi
@ -1436,12 +1538,12 @@ function CheckEnvironment {
if [ "$ENCRYPTION" == "yes" ]; then if [ "$ENCRYPTION" == "yes" ]; then
if ! type gpg > /dev/null 2>&1 ; then if ! type gpg > /dev/null 2>&1 ; then
Logger "gpg not present. Cannot encrypt backup files." "CRITICAL" Logger "gpg not present. Cannot encrypt backup files." "CRITICAL"
CAN_BACKUP_FILES=0 CAN_BACKUP_FILES=false
fi fi
else else
if ! type rsync > /dev/null 2>&1 ; then if ! type rsync > /dev/null 2>&1 ; then
Logger "rsync not present. Cannot backup files." "CRITICAL" Logger "rsync not present. Cannot backup files." "CRITICAL"
CAN_BACKUP_FILES=0 CAN_BACKUP_FILES=false
fi fi
fi fi
fi fi
@ -1477,11 +1579,11 @@ function CheckCurrentConfig {
if [ "$FILE_BACKUP" == "yes" ]; then if [ "$FILE_BACKUP" == "yes" ]; then
if [ "$DIRECTORY_LIST" == "" ] && [ "$RECURSIVE_DIRECTORY_LIST" == "" ]; then if [ "$DIRECTORY_LIST" == "" ] && [ "$RECURSIVE_DIRECTORY_LIST" == "" ]; then
Logger "No directories specified in config file, no files to backup." "ERROR" Logger "No directories specified in config file, no files to backup." "ERROR"
CAN_BACKUP_FILES=0 CAN_BACKUP_FILES=false
fi fi
fi fi
#TODO-v2.1: Add runtime variable tests (RSYNC_ARGS etc) #TODO-v2.1(ongoing WIP): Add runtime variable tests (RSYNC_ARGS etc)
if [ "$REMOTE_OPERATION" == "yes" ] && [ ! -f "$SSH_RSA_PRIVATE_KEY" ]; then if [ "$REMOTE_OPERATION" == "yes" ] && [ ! -f "$SSH_RSA_PRIVATE_KEY" ]; then
Logger "Cannot find rsa private key [$SSH_RSA_PRIVATE_KEY]. Cannot connect to remote system." "CRITICAL" Logger "Cannot find rsa private key [$SSH_RSA_PRIVATE_KEY]. Cannot connect to remote system." "CRITICAL"
exit 1 exit 1
@ -1560,7 +1662,7 @@ function ListDatabases {
local dbArray local dbArray
if [ $CAN_BACKUP_SQL -ne 1 ]; then if [ $CAN_BACKUP_SQL == false ]; then
Logger "Cannot list databases." "ERROR" Logger "Cannot list databases." "ERROR"
return 1 return 1
fi fi
@ -1583,7 +1685,7 @@ function ListDatabases {
fi fi
fi fi
if [ -f "$outputFile" ] && [ $CAN_BACKUP_SQL -eq 1 ]; then if [ -f "$outputFile" ] && [ $CAN_BACKUP_SQL == true ]; then
while read -r line; do while read -r line; do
while read -r name size; do dbName=$name; dbSize=$size; done <<< "$line" while read -r name size; do dbName=$name; dbSize=$size; done <<< "$line"
#db_name="${line% *}" #db_name="${line% *}"
@ -1625,7 +1727,7 @@ function ListDatabases {
Logger "Database exclude list: $SQL_EXCLUDED_TASKS" "DEBUG" Logger "Database exclude list: $SQL_EXCLUDED_TASKS" "DEBUG"
else else
Logger "Will not execute database backup." "ERROR" Logger "Will not execute database backup." "ERROR"
CAN_BACKUP_SQL=0 CAN_BACKUP_SQL=false
fi fi
} }
@ -1670,6 +1772,7 @@ function _ListRecursiveBackupDirectoriesRemote {
IFS=$PATH_SEPARATOR_CHAR read -r -a directories <<< "$RECURSIVE_DIRECTORY_LIST" IFS=$PATH_SEPARATOR_CHAR read -r -a directories <<< "$RECURSIVE_DIRECTORY_LIST"
for directory in "${directories[@]}"; do for directory in "${directories[@]}"; do
#TODO(med): Uses local home directory for remote lookup...
cmd=$SSH_CMD' "'$COMMAND_SUDO' '$REMOTE_FIND_CMD' -L '$directory'/ -mindepth 1 -maxdepth 1 -type d" >> '$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID' 2> '$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID cmd=$SSH_CMD' "'$COMMAND_SUDO' '$REMOTE_FIND_CMD' -L '$directory'/ -mindepth 1 -maxdepth 1 -type d" >> '$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID' 2> '$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID
Logger "cmd: $cmd" "DEBUG" Logger "cmd: $cmd" "DEBUG"
eval "$cmd" & eval "$cmd" &
@ -1696,6 +1799,8 @@ function ListRecursiveBackupDirectories {
local output_file local output_file
local file_exclude local file_exclude
local excluded
local fileArray local fileArray
Logger "Listing directories to backup." "NOTICE" Logger "Listing directories to backup." "NOTICE"
@ -1719,8 +1824,8 @@ function ListRecursiveBackupDirectories {
while read -r line; do while read -r line; do
file_exclude=0 file_exclude=0
IFS=$PATH_SEPARATOR_CHAR read -r -a fileArray <<< "$RECURSIVE_EXCLUDE_LIST" IFS=$PATH_SEPARATOR_CHAR read -r -a fileArray <<< "$RECURSIVE_EXCLUDE_LIST"
for k in "${fileArray[@]}"; do for excluded in "${fileArray[@]}"; do
if [ "$k" == "$line" ]; then if [ "$excluded" == "$line" ]; then
file_exclude=1 file_exclude=1
fi fi
done done
@ -1880,26 +1985,26 @@ function CreateStorageDirectories {
if [ "$SQL_BACKUP" != "no" ]; then if [ "$SQL_BACKUP" != "no" ]; then
_CreateDirectoryLocal "$SQL_STORAGE" _CreateDirectoryLocal "$SQL_STORAGE"
if [ $? != 0 ]; then if [ $? != 0 ]; then
CAN_BACKUP_SQL=0 CAN_BACKUP_SQL=false
fi fi
fi fi
if [ "$FILE_BACKUP" != "no" ]; then if [ "$FILE_BACKUP" != "no" ]; then
_CreateDirectoryLocal "$FILE_STORAGE" _CreateDirectoryLocal "$FILE_STORAGE"
if [ $? != 0 ]; then if [ $? != 0 ]; then
CAN_BACKUP_FILES=0 CAN_BACKUP_FILES=false
fi fi
fi fi
elif [ "$BACKUP_TYPE" == "push" ]; then elif [ "$BACKUP_TYPE" == "push" ]; then
if [ "$SQL_BACKUP" != "no" ]; then if [ "$SQL_BACKUP" != "no" ]; then
_CreateDirectoryRemote "$SQL_STORAGE" _CreateDirectoryRemote "$SQL_STORAGE"
if [ $? != 0 ]; then if [ $? != 0 ]; then
CAN_BACKUP_SQL=0 CAN_BACKUP_SQL=false
fi fi
fi fi
if [ "$FILE_BACKUP" != "no" ]; then if [ "$FILE_BACKUP" != "no" ]; then
_CreateDirectoryRemote "$FILE_STORAGE" _CreateDirectoryRemote "$FILE_STORAGE"
if [ $? != 0 ]; then if [ $? != 0 ]; then
CAN_BACKUP_FILES=0 CAN_BACKUP_FILES=false
fi fi
fi fi
fi fi
@ -1961,7 +2066,7 @@ function CheckDiskSpace {
GetDiskSpaceLocal "$SQL_STORAGE" GetDiskSpaceLocal "$SQL_STORAGE"
if [ $? != 0 ]; then if [ $? != 0 ]; then
SQL_DISK_SPACE=0 SQL_DISK_SPACE=0
CAN_BACKUP_SQL=0 CAN_BACKUP_SQL=false
else else
SQL_DISK_SPACE=$DISK_SPACE SQL_DISK_SPACE=$DISK_SPACE
SQL_DRIVE=$DRIVE SQL_DRIVE=$DRIVE
@ -1971,7 +2076,7 @@ function CheckDiskSpace {
GetDiskSpaceLocal "$FILE_STORAGE" GetDiskSpaceLocal "$FILE_STORAGE"
if [ $? != 0 ]; then if [ $? != 0 ]; then
FILE_DISK_SPACE=0 FILE_DISK_SPACE=0
CAN_BACKUP_FILES=0 CAN_BACKUP_FILES=false
else else
FILE_DISK_SPACE=$DISK_SPACE FILE_DISK_SPACE=$DISK_SPACE
FILE_DRIVE=$DRIVE FILE_DRIVE=$DRIVE
@ -2005,7 +2110,7 @@ function CheckDiskSpace {
TOTAL_FILES_SIZE=-1 TOTAL_FILES_SIZE=-1
fi fi
if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL -eq 1 ]; then if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL == true ]; then
if [ $SQL_DISK_SPACE -eq 0 ]; then if [ $SQL_DISK_SPACE -eq 0 ]; then
Logger "Storage space in [$SQL_STORAGE] reported to be 0Ko." "WARN" Logger "Storage space in [$SQL_STORAGE] reported to be 0Ko." "WARN"
fi fi
@ -2018,7 +2123,7 @@ function CheckDiskSpace {
Logger "SQL storage Space: $SQL_DISK_SPACE Ko - Databases size: $TOTAL_DATABASES_SIZE Ko" "NOTICE" Logger "SQL storage Space: $SQL_DISK_SPACE Ko - Databases size: $TOTAL_DATABASES_SIZE Ko" "NOTICE"
fi fi
if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES -eq 1 ]; then if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES == true ]; then
if [ $FILE_DISK_SPACE -eq 0 ]; then if [ $FILE_DISK_SPACE -eq 0 ]; then
Logger "Storage space in [$FILE_STORAGE] reported to be 0 Ko." "WARN" Logger "Storage space in [$FILE_STORAGE] reported to be 0 Ko." "WARN"
fi fi
@ -2049,7 +2154,7 @@ function _BackupDatabaseLocalToLocal {
local dry_sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS > /dev/null 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID" local dry_sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS > /dev/null 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID"
local sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS > $SQL_STORAGE/$database.sql$COMPRESSION_EXTENSION 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID" local sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS > $SQL_STORAGE/$database.sql$COMPRESSION_EXTENSION 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID"
if [ $_DRYRUN -ne 1 ]; then if [ $_DRYRUN == false ]; then
Logger "cmd: $sql_cmd" "DEBUG" Logger "cmd: $sql_cmd" "DEBUG"
eval "$sql_cmd" & eval "$sql_cmd" &
else else
@ -2082,7 +2187,7 @@ function _BackupDatabaseLocalToRemote {
local dry_sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS > /dev/null 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID" local dry_sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS > /dev/null 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID"
local sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS | $SSH_CMD '$COMMAND_SUDO tee \"$SQL_STORAGE/$database.sql$COMPRESSION_EXTENSION\" > /dev/null' 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID" local sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS | $SSH_CMD '$COMMAND_SUDO tee \"$SQL_STORAGE/$database.sql$COMPRESSION_EXTENSION\" > /dev/null' 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID"
if [ $_DRYRUN -ne 1 ]; then if [ $_DRYRUN == false ]; then
Logger "cmd: $sql_cmd" "DEBUG" Logger "cmd: $sql_cmd" "DEBUG"
eval "$sql_cmd" & eval "$sql_cmd" &
else else
@ -2115,7 +2220,7 @@ function _BackupDatabaseRemoteToLocal {
local dry_sql_cmd=$SSH_CMD' "mysqldump -u '$SQL_USER' '$export_options' --database '$database' '$COMPRESSION_PROGRAM' '$COMPRESSION_OPTIONS'" > /dev/null 2> "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID'"' local dry_sql_cmd=$SSH_CMD' "mysqldump -u '$SQL_USER' '$export_options' --database '$database' '$COMPRESSION_PROGRAM' '$COMPRESSION_OPTIONS'" > /dev/null 2> "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID'"'
local sql_cmd=$SSH_CMD' "mysqldump -u '$SQL_USER' '$export_options' --database '$database' '$COMPRESSION_PROGRAM' '$COMPRESSION_OPTIONS'" > "'$SQL_STORAGE/$database.sql$COMPRESSION_EXTENSION'" 2> "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID'"' local sql_cmd=$SSH_CMD' "mysqldump -u '$SQL_USER' '$export_options' --database '$database' '$COMPRESSION_PROGRAM' '$COMPRESSION_OPTIONS'" > "'$SQL_STORAGE/$database.sql$COMPRESSION_EXTENSION'" 2> "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID'"'
if [ $_DRYRUN -ne 1 ]; then if [ $_DRYRUN == false ]; then
Logger "cmd: $sql_cmd" "DEBUG" Logger "cmd: $sql_cmd" "DEBUG"
eval "$sql_cmd" & eval "$sql_cmd" &
else else
@ -2140,9 +2245,9 @@ function BackupDatabase {
# Hack to prevent warning on table mysql.events, some mysql versions don't support --skip-events, prefer using --ignore-table # Hack to prevent warning on table mysql.events, some mysql versions don't support --skip-events, prefer using --ignore-table
if [ "$database" == "mysql" ]; then if [ "$database" == "mysql" ]; then
mysql_options='--skip-lock-tables --single-transaction --ignore-table=mysql.event' mysql_options="$MYSQLDUMP_OPTIONS --ignore-table=mysql.event"
else else
mysql_options='--skip-lock-tables --single-transaction' mysql_options="$MYSQLDUMP_OPTIONS"
fi fi
if [ "$BACKUP_TYPE" == "local" ]; then if [ "$BACKUP_TYPE" == "local" ]; then
@ -2193,8 +2298,7 @@ function EncrpytFiles {
__CheckArguments 2 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 2 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
#WIP: template code to split into local & remote code #gpg here ?
#crypt_cmd source temp #crypt_cmd source temp
# Send files to remote, rotate & copy # Send files to remote, rotate & copy
} }
@ -2225,19 +2329,19 @@ function Rsync {
# Creating subdirectories because rsync cannot handle multiple subdirectory creation # Creating subdirectories because rsync cannot handle multiple subdirectory creation
if [ "$BACKUP_TYPE" == "local" ]; then if [ "$BACKUP_TYPE" == "local" ]; then
_CreateDirectoryLocal "$file_storage_path" _CreateDirectoryLocal "$file_storage_path"
rsync_cmd="$(type -p $RSYNC_EXECUTABLE) $RSYNC_ARGS $RSYNC_DRY_ARG $RSYNC_ATTR_ARGS $RSYNC_TYPE_ARGS $RSYNC_NO_RECURSE_ARGS --stats $RSYNC_DELETE $RSYNC_PATTERNS $RSYNC_PARTIAL_EXCLUDE --rsync-path=\"$RSYNC_PATH\" \"$backup_directory\" \"$file_storage_path\" > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1" rsync_cmd="$(type -p $RSYNC_EXECUTABLE) $RSYNC_ARGS $RSYNC_DRY_ARG $RSYNC_ATTR_ARGS $RSYNC_TYPE_ARGS $RSYNC_NO_RECURSE_ARGS $RSYNC_DELETE $RSYNC_PATTERNS $RSYNC_PARTIAL_EXCLUDE --rsync-path=\"$RSYNC_PATH\" \"$backup_directory\" \"$file_storage_path\" > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1"
elif [ "$BACKUP_TYPE" == "pull" ]; then elif [ "$BACKUP_TYPE" == "pull" ]; then
_CreateDirectoryLocal "$file_storage_path" _CreateDirectoryLocal "$file_storage_path"
CheckConnectivity3rdPartyHosts CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost CheckConnectivityRemoteHost
backup_directory=$(EscapeSpaces "$backup_directory") backup_directory=$(EscapeSpaces "$backup_directory")
rsync_cmd="$(type -p $RSYNC_EXECUTABLE) $RSYNC_ARGS $RSYNC_DRY_ARG $RSYNC_ATTR_ARGS $RSYNC_TYPE_ARGS $RSYNC_NO_RECURSE_ARGS --stats $RSYNC_DELETE $RSYNC_PATTERNS $RSYNC_PARTIAL_EXCLUDE --rsync-path=\"$RSYNC_PATH\" -e \"$RSYNC_SSH_CMD\" \"$REMOTE_USER@$REMOTE_HOST:$backup_directory\" \"$file_storage_path\" > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1" rsync_cmd="$(type -p $RSYNC_EXECUTABLE) $RSYNC_ARGS $RSYNC_DRY_ARG $RSYNC_ATTR_ARGS $RSYNC_TYPE_ARGS $RSYNC_NO_RECURSE_ARGS $RSYNC_DELETE $RSYNC_PATTERNS $RSYNC_PARTIAL_EXCLUDE --rsync-path=\"$RSYNC_PATH\" -e \"$RSYNC_SSH_CMD\" \"$REMOTE_USER@$REMOTE_HOST:$backup_directory\" \"$file_storage_path\" > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1"
elif [ "$BACKUP_TYPE" == "push" ]; then elif [ "$BACKUP_TYPE" == "push" ]; then
CheckConnectivity3rdPartyHosts CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost CheckConnectivityRemoteHost
file_storage_path=$(EscapeSpaces "$file_storage_path") file_storage_path=$(EscapeSpaces "$file_storage_path")
_CreateDirectoryRemote "$file_storage_path" _CreateDirectoryRemote "$file_storage_path"
rsync_cmd="$(type -p $RSYNC_EXECUTABLE) $RSYNC_ARGS $RSYNC_DRY_ARG $RSYNC_ATTR_ARGS $RSYNC_TYPE_ARGS $RSYNC_NO_RECURSE_ARGS --stats $RSYNC_DELETE $RSYNC_PATTERNS $RSYNC_PARTIAL_EXCLUDE --rsync-path=\"$RSYNC_PATH\" -e \"$RSYNC_SSH_CMD\" \"$backup_directory\" \"$REMOTE_USER@$REMOTE_HOST:$file_storage_path\" > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1" rsync_cmd="$(type -p $RSYNC_EXECUTABLE) $RSYNC_ARGS $RSYNC_DRY_ARG $RSYNC_ATTR_ARGS $RSYNC_TYPE_ARGS $RSYNC_NO_RECURSE_ARGS $RSYNC_DELETE $RSYNC_PATTERNS $RSYNC_PARTIAL_EXCLUDE --rsync-path=\"$RSYNC_PATH\" -e \"$RSYNC_SSH_CMD\" \"$backup_directory\" \"$REMOTE_USER@$REMOTE_HOST:$file_storage_path\" > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1"
fi fi
Logger "cmd: $rsync_cmd" "DEBUG" Logger "cmd: $rsync_cmd" "DEBUG"
@ -2251,48 +2355,6 @@ function Rsync {
fi fi
} }
function Duplicity {
local backup_directory="${1}" # Which directory to backup
local is_recursive="${2}" # Backup only files at toplevel of directory
__CheckArguments 2 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
local file_storage_path
local duplicity_cmd
Logger "Encrpytion not supported yet ! No backup done." "CRITICAL"
return 1
if [ "$KEEP_ABSOLUTE_PATHS" == "yes" ]; then
file_storage_path="$(dirname $FILE_STORAGE$backup_directory)"
else
file_storage_path="$FILE_STORAGE"
fi
if [ "$BACKUP_TYPE" == "local" ]; then
duplicity_cmd=""
elif [ "$BACKUP_TYPE" == "pull" ]; then
CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost
duplicity_cmd=""
elif [ "$BACKUP_TYPE" == "push" ]; then
CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost
duplicity_cmd=""
fi
Logger "cmd: $duplicity_cmd" "DEBUG"
eval "$duplicity_cmd" &
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_FILE_TASK $HARD_MAX_EXEC_TIME_FILE_TASK ${FUNCNAME[0]} true $KEEP_LOGGING
if [ $? != 0 ]; then
Logger "Failed to backup [$backup_directory] to [$file_storage_path]." "ERROR"
Logger "Command output:\n $(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
else
Logger "File backup succeed." "NOTICE"
fi
}
function FilesBackup { function FilesBackup {
__CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
@ -2303,7 +2365,9 @@ function FilesBackup {
for backupTask in "${backupTasks[@]}"; do for backupTask in "${backupTasks[@]}"; do
Logger "Beginning file backup of [$backupTask]." "NOTICE" Logger "Beginning file backup of [$backupTask]." "NOTICE"
if [ "$ENCRYPTION" == "yes" ]; then if [ "$ENCRYPTION" == "yes" ]; then
Duplicity "$backupTask" "recurse" Dummy
#Encrpyt files recursively
#Rsync encrypted files instead of original ones
else else
Rsync "$backupTask" "recurse" Rsync "$backupTask" "recurse"
fi fi
@ -2314,7 +2378,9 @@ function FilesBackup {
for backupTask in "${backupTasks[@]}"; do for backupTask in "${backupTasks[@]}"; do
Logger "Beginning non recursive file backup of [$backupTask]." "NOTICE" Logger "Beginning non recursive file backup of [$backupTask]." "NOTICE"
if [ "$ENCRYPTION" == "yes" ]; then if [ "$ENCRYPTION" == "yes" ]; then
Duplicity "$backupTask" "no-recurse" Dummy
#Encrpyt files non recursively
#Rsync encrypted files instead of original ones
else else
Rsync "$backupTask" "no-recurse" Rsync "$backupTask" "no-recurse"
fi fi
@ -2326,7 +2392,9 @@ function FilesBackup {
# Backup sub directories of recursive directories # Backup sub directories of recursive directories
Logger "Beginning recursive file backup of [$backupTask]." "NOTICE" Logger "Beginning recursive file backup of [$backupTask]." "NOTICE"
if [ "$ENCRYPTION" == "yes" ]; then if [ "$ENCRYPTION" == "yes" ]; then
Duplicity "$backupTask" "recurse" Dummy
#Encrpyt files recursively
#Rsync encrypted files instead of original ones
else else
Rsync "$backupTask" "recurse" Rsync "$backupTask" "recurse"
fi fi
@ -2594,7 +2662,7 @@ function Init {
## Add update to default RSYNC_ARGS ## Add update to default RSYNC_ARGS
RSYNC_ARGS=$RSYNC_ARGS" -u" RSYNC_ARGS=$RSYNC_ARGS" -u"
if [ $_VERBOSE -eq 1 ]; then if [ $_VERBOSE == true ]; then
RSYNC_ARGS=$RSYNC_ARGS" -i" RSYNC_ARGS=$RSYNC_ARGS" -i"
fi fi
@ -2602,7 +2670,7 @@ function Init {
RSYNC_ARGS=$RSYNC_ARGS" --delete" RSYNC_ARGS=$RSYNC_ARGS" --delete"
fi fi
if [ $stats -eq 1 ]; then if [ $stats == true ]; then
RSYNC_ARGS=$RSYNC_ARGS" --stats" RSYNC_ARGS=$RSYNC_ARGS" --stats"
fi fi
@ -2613,10 +2681,10 @@ function Init {
function Main { function Main {
__CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL -eq 1 ]; then if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL == true ]; then
ListDatabases ListDatabases
fi fi
if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES -eq 1 ]; then if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES == true ]; then
ListRecursiveBackupDirectories ListRecursiveBackupDirectories
if [ "$GET_BACKUP_SIZE" != "no" ]; then if [ "$GET_BACKUP_SIZE" != "no" ]; then
GetDirectoriesSize GetDirectoriesSize
@ -2625,21 +2693,27 @@ function Main {
fi fi
fi fi
# Expand ~ if exists
FILE_STORAGE="${FILE_STORAGE/#\~/$HOME}"
SQL_STORAGE="${SQL_STORAGE/#\~/$HOME}"
SSH_RSA_PRIVATE_KEY="${SSH_RSA_PRIVATE_KEY/#\~/$HOME}"
ENCRYPT_PUBKEY="${ENCRPYT_PUBKEY/#\~/$HOME}"
if [ "$CREATE_DIRS" != "no" ]; then if [ "$CREATE_DIRS" != "no" ]; then
CreateStorageDirectories CreateStorageDirectories
fi fi
CheckDiskSpace CheckDiskSpace
# Actual backup process # Actual backup process
if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL -eq 1 ]; then if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL == true ]; then
if [ $_DRYRUN -ne 1 ] && [ "$ROTATE_SQL_BACKUPS" == "yes" ]; then if [ $_DRYRUN == false ] && [ "$ROTATE_SQL_BACKUPS" == "yes" ]; then
RotateBackups "$SQL_STORAGE" "$ROTATE_SQL_COPIES" RotateBackups "$SQL_STORAGE" "$ROTATE_SQL_COPIES"
fi fi
BackupDatabases BackupDatabases
fi fi
if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES -eq 1 ]; then if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES == true ]; then
if [ $_DRYRUN -ne 1 ] && [ "$ROTATE_FILE_BACKUPS" == "yes" ]; then if [ $_DRYRUN == false ] && [ "$ROTATE_FILE_BACKUPS" == "yes" ]; then
RotateBackups "$FILE_STORAGE" "$ROTATE_FILE_COPIES" RotateBackups "$FILE_STORAGE" "$ROTATE_FILE_COPIES"
fi fi
## Add Rsync include / exclude patterns ## Add Rsync include / exclude patterns
@ -2675,11 +2749,11 @@ function Usage {
} }
# Command line argument flags # Command line argument flags
_DRYRUN=0 _DRYRUN=false
_SILENT=0 _SILENT=false
no_maxtime=0 no_maxtime=false
stats=0 stats=false
PARTIAL=0 PARTIAL=no
function GetCommandlineArguments { function GetCommandlineArguments {
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
@ -2689,22 +2763,22 @@ function GetCommandlineArguments {
for i in "$@"; do for i in "$@"; do
case $i in case $i in
--dry) --dry)
_DRYRUN=1 _DRYRUN=true
;; ;;
--silent) --silent)
_SILENT=1 _SILENT=true
;; ;;
--verbose) --verbose)
_VERBOSE=1 _VERBOSE=true
;; ;;
--stats) --stats)
stats=1 stats=false
;; ;;
--partial) --partial)
PARTIAL="yes" PARTIAL="yes"
;; ;;
--no-maxtime) --no-maxtime)
no_maxtime=1 no_maxtime=true
;; ;;
--delete) --delete)
DELETE_VANISHED_FILES="yes" DELETE_VANISHED_FILES="yes"
@ -2724,8 +2798,8 @@ LoadConfigFile "$1"
if [ "$LOGFILE" == "" ]; then if [ "$LOGFILE" == "" ]; then
if [ -w /var/log ]; then if [ -w /var/log ]; then
LOG_FILE="/var/log/$PROGRAM.$INSTANCE_ID.log" LOG_FILE="/var/log/$PROGRAM.$INSTANCE_ID.log"
elif ([ "$HOME" != "" ] && [ -w "$HOME" ]); then elif ([ "${HOME}" != "" ] && [ -w "{$HOME}" ]); then
LOG_FILE="$HOME/$PROGRAM.$INSTANCE_ID.log" LOG_FILE="${HOME}/$PROGRAM.$INSTANCE_ID.log"
else else
LOG_FILE=./$PROGRAM.$INSTANCE_ID.log LOG_FILE=./$PROGRAM.$INSTANCE_ID.log
fi fi
@ -2757,7 +2831,7 @@ if [ "$REMOTE_OPERATION" == "yes" ]; then
InitRemoteOSSettings InitRemoteOSSettings
fi fi
if [ $no_maxtime -eq 1 ]; then if [ $no_maxtime == true ]; then
SOFT_MAX_EXEC_TIME_DB_TASK=0 SOFT_MAX_EXEC_TIME_DB_TASK=0
SOFT_MAX_EXEC_TIME_FILE_TASK=0 SOFT_MAX_EXEC_TIME_FILE_TASK=0
HARD_MAX_EXEC_TIME_DB_TASK=0 HARD_MAX_EXEC_TIME_DB_TASK=0

View File

@ -3,7 +3,7 @@ SUBPROGRAM=obackup
PROGRAM="$SUBPROGRAM-batch" # Batch program to run osync / obackup instances sequentially and rerun failed ones PROGRAM="$SUBPROGRAM-batch" # Batch program to run osync / obackup instances sequentially and rerun failed ones
AUTHOR="(L) 2013-2016 by Orsiris de Jong" AUTHOR="(L) 2013-2016 by Orsiris de Jong"
CONTACT="http://www.netpower.fr - ozy@netpower.fr" CONTACT="http://www.netpower.fr - ozy@netpower.fr"
PROGRAM_BUILD=2016081704 PROGRAM_BUILD=2016082901
## Runs an osync /obackup instance for every conf file found ## Runs an osync /obackup instance for every conf file found
## If an instance fails, run it again if time permits ## If an instance fails, run it again if time permits
@ -68,7 +68,7 @@ function CheckEnvironment {
then then
SUBPROGRAM_EXECUTABLE=/usr/local/bin/$SUBPROGRAM.sh SUBPROGRAM_EXECUTABLE=/usr/local/bin/$SUBPROGRAM.sh
else else
Logger "Could not find $SUBPROGRAM.sh" "CRITICAL" Logger "Could not find [/usr/local/bin/$SUBPROGRAM.sh]" "CRITICAL"
exit 1 exit 1
fi fi
else else

View File

@ -5,15 +5,19 @@ PROGRAM="obackup"
AUTHOR="(C) 2013-2016 by Orsiris de Jong" AUTHOR="(C) 2013-2016 by Orsiris de Jong"
CONTACT="http://www.netpower.fr/obackup - ozy@netpower.fr" CONTACT="http://www.netpower.fr/obackup - ozy@netpower.fr"
PROGRAM_VERSION=2.1-dev PROGRAM_VERSION=2.1-dev
PROGRAM_BUILD=2016082603 PROGRAM_BUILD=2016083002
IS_STABLE=no IS_STABLE=yes
#### MINIMAL-FUNCTION-SET BEGIN #### #### MINIMAL-FUNCTION-SET BEGIN ####
## FUNC_BUILD=2016082605 ## FUNC_BUILD=2016083003
## BEGIN Generic functions for osync & obackup written in 2013-2016 by Orsiris de Jong - http://www.netpower.fr - ozy@netpower.fr ## BEGIN Generic bash functions written in 2013-2016 by Orsiris de Jong - http://www.netpower.fr - ozy@netpower.fr
## To use in a program, define the following variables:
## PROGRAM=program-name
## INSTANCE_ID=program-instance-name
## _DEBUG=yes/no
## type -p does not work on platforms other than linux (bash). If if does not work, always assume output is not a zero exitcode
if ! type "$BASH" > /dev/null; then if ! type "$BASH" > /dev/null; then
echo "Please run this script only with bash shell. Tested on bash >= 3.2" echo "Please run this script only with bash shell. Tested on bash >= 3.2"
exit 127 exit 127
@ -26,31 +30,32 @@ export LC_ALL=C
MAIL_ALERT_MSG="Execution of $PROGRAM instance $INSTANCE_ID on $(date) has warnings/errors." MAIL_ALERT_MSG="Execution of $PROGRAM instance $INSTANCE_ID on $(date) has warnings/errors."
# Environment variables that can be overriden by programs # Environment variables that can be overriden by programs
_DRYRUN=0 _DRYRUN=false
_SILENT=0 _SILENT=false
_VERBOSE=false
_LOGGER_PREFIX="date" _LOGGER_PREFIX="date"
_LOGGER_STDERR=0 _LOGGER_STDERR=false
if [ "$KEEP_LOGGING" == "" ]; then if [ "$KEEP_LOGGING" == "" ]; then
KEEP_LOGGING=1801 KEEP_LOGGING=1801
fi fi
# Initial error status, logging 'WARN', 'ERROR' or 'CRITICAL' will enable alerts flags # Initial error status, logging 'WARN', 'ERROR' or 'CRITICAL' will enable alerts flags
ERROR_ALERT=0 ERROR_ALERT=false
WARN_ALERT=0 WARN_ALERT=false
# Current log # Log from current run
CURRENT_LOG CURRENT_LOG=""
## allow debugging from command line with _DEBUG=yes ## allow debugging from command line with _DEBUG=yes
if [ ! "$_DEBUG" == "yes" ]; then if [ ! "$_DEBUG" == "yes" ]; then
_DEBUG=no _DEBUG=no
SLEEP_TIME=.05 # Tested under linux and FreeBSD bash, #TODO tests on cygwin / msys SLEEP_TIME=.05 # Tested under linux and FreeBSD bash, #TODO tests on cygwin / msys
_VERBOSE=0 _VERBOSE=false
else else
SLEEP_TIME=1 SLEEP_TIME=1
trap 'TrapError ${LINENO} $?' ERR trap 'TrapError ${LINENO} $?' ERR
_VERBOSE=1 _VERBOSE=true
fi fi
SCRIPT_PID=$$ SCRIPT_PID=$$
@ -58,6 +63,10 @@ SCRIPT_PID=$$
LOCAL_USER=$(whoami) LOCAL_USER=$(whoami)
LOCAL_HOST=$(hostname) LOCAL_HOST=$(hostname)
if [ "$PROGRAM" == "" ]; then
PROGRAM="ofunctions"
fi
## Default log file until config file is loaded ## Default log file until config file is loaded
if [ -w /var/log ]; then if [ -w /var/log ]; then
LOG_FILE="/var/log/$PROGRAM.log" LOG_FILE="/var/log/$PROGRAM.log"
@ -99,17 +108,21 @@ function _Logger {
echo -e "$lvalue" >> "$LOG_FILE" echo -e "$lvalue" >> "$LOG_FILE"
CURRENT_LOG="$CURRENT_LOG"$'\n'"$lvalue" CURRENT_LOG="$CURRENT_LOG"$'\n'"$lvalue"
if [ "$_LOGGER_STDERR" -eq 1 ]; then if [ $_LOGGER_STDERR == true ]; then
cat <<< "$evalue" 1>&2 cat <<< "$evalue" 1>&2
elif [ "$_SILENT" -eq 0 ]; then elif [ "$_SILENT" == false ]; then
echo -e "$svalue" echo -e "$svalue"
fi fi
} }
# General log function with log levels # General log function with log levels:
# CRITICAL, ERROR, WARN are colored in stdout, prefixed in stderr
# NOTICE is standard level
# VERBOSE is only sent to stdout / stderr if _VERBOSE=true
# DEBUG & PARANOIA_DEBUG are only sent if _DEBUG=yes
function Logger { function Logger {
local value="${1}" # Sentence to log (in double quotes) local value="${1}" # Sentence to log (in double quotes)
local level="${2}" # Log level: PARANOIA_DEBUG, DEBUG, NOTICE, WARN, ERROR, CRITIAL local level="${2}" # Log level: PARANOIA_DEBUG, DEBUG, VERBOSE, NOTICE, WARN, ERROR, CRITIAL
if [ "$_LOGGER_PREFIX" == "time" ]; then if [ "$_LOGGER_PREFIX" == "time" ]; then
prefix="TIME: $SECONDS - " prefix="TIME: $SECONDS - "
@ -121,26 +134,31 @@ function Logger {
if [ "$level" == "CRITICAL" ]; then if [ "$level" == "CRITICAL" ]; then
_Logger "$prefix\e[41m$value\e[0m" "$prefix$level:$value" "$level:$value" _Logger "$prefix\e[41m$value\e[0m" "$prefix$level:$value" "$level:$value"
ERROR_ALERT=1 ERROR_ALERT=true
return return
elif [ "$level" == "ERROR" ]; then elif [ "$level" == "ERROR" ]; then
_Logger "$prefix\e[91m$value\e[0m" "$prefix$level:$value" "$level:$value" _Logger "$prefix\e[91m$value\e[0m" "$prefix$level:$value" "$level:$value"
ERROR_ALERT=1 ERROR_ALERT=true
return return
elif [ "$level" == "WARN" ]; then elif [ "$level" == "WARN" ]; then
_Logger "$prefix\e[93m$value\e[0m" "$prefix$level:$value" "$level:$value" _Logger "$prefix\e[93m$value\e[0m" "$prefix$level:$value" "$level:$value"
WARN_ALERT=1 WARN_ALERT=true
return return
elif [ "$level" == "NOTICE" ]; then elif [ "$level" == "NOTICE" ]; then
_Logger "$prefix$value" _Logger "$prefix$value"
return return
elif [ "$level" == "VERBOSE" ]; then
if [ $_VERBOSE == true ]; then
_Logger "$prefix$value"
fi
return
elif [ "$level" == "DEBUG" ]; then elif [ "$level" == "DEBUG" ]; then
if [ "$_DEBUG" == "yes" ]; then if [ "$_DEBUG" == "yes" ]; then
_Logger "$prefix$value" _Logger "$prefix$value"
return return
fi fi
else else
_Logger "\e[41mLogger function called without proper loglevel.\e[0m" _Logger "\e[41mLogger function called without proper loglevel [$level].\e[0m"
_Logger "$prefix$value" _Logger "$prefix$value"
fi fi
} }
@ -163,7 +181,7 @@ function QuickLogger {
local value="${1}" local value="${1}"
if [ "$_SILENT" -eq 1 ]; then if [ $_SILENT == true ]; then
_QuickLogger "$value" "log" _QuickLogger "$value" "log"
else else
_QuickLogger "$value" "stdout" _QuickLogger "$value" "stdout"
@ -257,9 +275,9 @@ function SendAlert {
fi fi
body="$MAIL_ALERT_MSG"$'\n\n'"$CURRENT_LOG" body="$MAIL_ALERT_MSG"$'\n\n'"$CURRENT_LOG"
if [ $ERROR_ALERT -eq 1 ]; then if [ $ERROR_ALERT == true ]; then
subject="Error alert for $INSTANCE_ID" subject="Error alert for $INSTANCE_ID"
elif [ $WARN_ALERT -eq 1 ]; then elif [ $WARN_ALERT == true ]; then
subject="Warning alert for $INSTANCE_ID" subject="Warning alert for $INSTANCE_ID"
else else
subject="Alert for $INSTANCE_ID" subject="Alert for $INSTANCE_ID"
@ -510,7 +528,7 @@ function TrapError {
local job="$0" local job="$0"
local line="$1" local line="$1"
local code="${2:-1}" local code="${2:-1}"
if [ $_SILENT -eq 0 ]; then if [ $_SILENT == false ]; then
echo -e " /!\ ERROR in ${job}: Near line ${line}, exit code ${code}" echo -e " /!\ ERROR in ${job}: Near line ${line}, exit code ${code}"
fi fi
} }
@ -535,7 +553,7 @@ function LoadConfigFile {
} }
function Spinner { function Spinner {
if [ $_SILENT -eq 1 ]; then if [ $_SILENT == true ]; then
return 0 return 0
fi fi
@ -576,6 +594,7 @@ function joinString {
# Time control function for background processes, suitable for multiple synchronous processes # Time control function for background processes, suitable for multiple synchronous processes
# Fills a global variable called WAIT_FOR_TASK_COMPLETION that contains list of failed pids in format pid1:result1;pid2:result2 # Fills a global variable called WAIT_FOR_TASK_COMPLETION that contains list of failed pids in format pid1:result1;pid2:result2
# Warning: Don't imbricate this function into another run if you plan to use the global variable output # Warning: Don't imbricate this function into another run if you plan to use the global variable output
function WaitForTaskCompletion { function WaitForTaskCompletion {
local pids="${1}" # pids to wait for, separated by semi-colon local pids="${1}" # pids to wait for, separated by semi-colon
local soft_max_time="${2}" # If program with pid $pid takes longer than $soft_max_time seconds, will log a warning, unless $soft_max_time equals 0. local soft_max_time="${2}" # If program with pid $pid takes longer than $soft_max_time seconds, will log a warning, unless $soft_max_time equals 0.
@ -585,7 +604,7 @@ function WaitForTaskCompletion {
local keep_logging="${6:-0}" # Log a standby message every X seconds. Set to zero to disable logging local keep_logging="${6:-0}" # Log a standby message every X seconds. Set to zero to disable logging
local soft_alert=0 # Does a soft alert need to be triggered, if yes, send an alert once local soft_alert=false # Does a soft alert need to be triggered, if yes, send an alert once
local log_ttime=0 # local time instance for comparaison local log_ttime=0 # local time instance for comparaison
local seconds_begin=$SECONDS # Seconds since the beginning of the script local seconds_begin=$SECONDS # Seconds since the beginning of the script
@ -594,9 +613,14 @@ function WaitForTaskCompletion {
local retval=0 # return value of monitored pid process local retval=0 # return value of monitored pid process
local errorcount=0 # Number of pids that finished with errors local errorcount=0 # Number of pids that finished with errors
local pid # Current pid working on
local pidCount # number of given pids local pidCount # number of given pids
local pidState # State of the process local pidState # State of the process
local pidsArray # Array of currently running pids
local newPidsArray # New array of currently running pids
IFS=';' read -a pidsArray <<< "$pids" IFS=';' read -a pidsArray <<< "$pids"
pidCount=${#pidsArray[@]} pidCount=${#pidsArray[@]}
@ -622,9 +646,9 @@ function WaitForTaskCompletion {
fi fi
if [ $exec_time -gt $soft_max_time ]; then if [ $exec_time -gt $soft_max_time ]; then
if [ $soft_alert -eq 0 ] && [ $soft_max_time -ne 0 ]; then if [ $soft_alert == true ] && [ $soft_max_time -ne 0 ]; then
Logger "Max soft execution time exceeded for task [$caller_name] with pids [$(joinString , ${pidsArray[@]})]." "WARN" Logger "Max soft execution time exceeded for task [$caller_name] with pids [$(joinString , ${pidsArray[@]})]." "WARN"
soft_alert=1 soft_alert=true
SendAlert true SendAlert true
fi fi
@ -644,6 +668,7 @@ function WaitForTaskCompletion {
fi fi
for pid in "${pidsArray[@]}"; do for pid in "${pidsArray[@]}"; do
if [ $(IsNumeric $pid) -eq 1 ]; then
if kill -0 $pid > /dev/null 2>&1; then if kill -0 $pid > /dev/null 2>&1; then
# Handle uninterruptible sleep state or zombies by ommiting them from running process array (How to kill that is already dead ? :) # Handle uninterruptible sleep state or zombies by ommiting them from running process array (How to kill that is already dead ? :)
#TODO(high): have this tested on *BSD, Mac & Win #TODO(high): have this tested on *BSD, Mac & Win
@ -657,17 +682,20 @@ function WaitForTaskCompletion {
retval=$? retval=$?
if [ $retval -ne 0 ]; then if [ $retval -ne 0 ]; then
errorcount=$((errorcount+1)) errorcount=$((errorcount+1))
Logger "${FUNCNAME[0]} called by [$caller_name] finished monitoring [$pid] with exitcode [$result]." "DEBUG" Logger "${FUNCNAME[0]} called by [$caller_name] finished monitoring [$pid] with exitcode [$retval]." "DEBUG"
if [ "$WAIT_FOR_TASK_COMPLETION" == "" ]; then if [ "$WAIT_FOR_TASK_COMPLETION" == "" ]; then
WAIT_FOR_TASK_COMPLETION="$pid:$result" WAIT_FOR_TASK_COMPLETION="$pid:$retval"
else else
WAIT_FOR_TASK_COMPLETION=";$pid:$result" WAIT_FOR_TASK_COMPLETION=";$pid:$retval"
fi
fi fi
fi fi
fi fi
done done
pidsArray=("${newPidsArray[@]}") pidsArray=("${newPidsArray[@]}")
# Trivial wait time for bash to not eat up all CPU
sleep $SLEEP_TIME sleep $SLEEP_TIME
done done
@ -680,6 +708,70 @@ function WaitForTaskCompletion {
fi fi
} }
# Take a list of commands to run, runs them sequentially with numberOfProcesses commands simultaneously runs
# Returns the number of non zero exit codes from commands
function ParallelExec {
local numberOfProcesses="${1}" # Number of simultaneous commands to run
local commandsArg="${2}" # Semi-colon separated list of commands
local pid
local runningPids=0
local counter=0
local commandsArray
local pidsArray
local newPidsArray
local retval
local retvalAll=0
local pidState
local commandsArrayPid
IFS=';' read -r -a commandsArray <<< "$commandsArg"
Logger "Runnning ${#commandsArray[@]} commands in $numberOfProcesses simultaneous processes." "DEBUG"
while [ $counter -lt "${#commandsArray[@]}" ] || [ ${#pidsArray[@]} -gt 0 ]; do
while [ $counter -lt "${#commandsArray[@]}" ] && [ ${#pidsArray[@]} -lt $numberOfProcesses ]; do
Logger "Running command [${commandsArray[$counter]}]." "DEBUG"
eval "${commandsArray[$counter]}" &
pid=$!
pidsArray+=($pid)
commandsArrayPid[$pid]="${commandsArray[$counter]}"
counter=$((counter+1))
done
newPidsArray=()
for pid in "${pidsArray[@]}"; do
if [ $(IsNumeric $pid) -eq 1 ]; then
# Handle uninterruptible sleep state or zombies by ommiting them from running process array (How to kill that is already dead ? :)
if kill -0 $pid > /dev/null 2>&1; then
pidState=$(ps -p$pid -o state= 2 > /dev/null)
if [ "$pidState" != "D" ] && [ "$pidState" != "Z" ]; then
newPidsArray+=($pid)
fi
else
# pid is dead, get it's exit code from wait command
wait $pid
retval=$?
if [ $retval -ne 0 ]; then
Logger "Command [${commandsArrayPid[$pid]}] failed with exit code [$retval]." "ERROR"
retvalAll=$((retvalAll+1))
fi
fi
fi
done
pidsArray=("${newPidsArray[@]}")
# Trivial wait time for bash to not eat up all CPU
sleep $SLEEP_TIME
done
return $retvalAll
}
function CleanUp { function CleanUp {
if [ "$_DEBUG" != "yes" ]; then if [ "$_DEBUG" != "yes" ]; then
@ -863,7 +955,7 @@ function RunLocalCommand {
local command="${1}" # Command to run local command="${1}" # Command to run
local hard_max_time="${2}" # Max time to wait for command to compleet local hard_max_time="${2}" # Max time to wait for command to compleet
if [ $_DRYRUN -ne 0 ]; then if [ $_DRYRUN == true ]; then
Logger "Dryrun: Local command [$command] not run." "NOTICE" Logger "Dryrun: Local command [$command] not run." "NOTICE"
return 0 return 0
fi fi
@ -878,7 +970,7 @@ function RunLocalCommand {
Logger "Command failed." "ERROR" Logger "Command failed." "ERROR"
fi fi
if [ $_VERBOSE -eq 1 ] || [ $retval -ne 0 ]; then if [ $_VERBOSE == true ] || [ $retval -ne 0 ]; then
Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "NOTICE" Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "NOTICE"
fi fi
@ -895,7 +987,7 @@ function RunRemoteCommand {
CheckConnectivity3rdPartyHosts CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost CheckConnectivityRemoteHost
if [ $_DRYRUN -ne 0 ]; then if [ $_DRYRUN == true ]; then
Logger "Dryrun: Local command [$command] not run." "NOTICE" Logger "Dryrun: Local command [$command] not run." "NOTICE"
return 0 return 0
fi fi
@ -912,7 +1004,7 @@ function RunRemoteCommand {
Logger "Command failed." "ERROR" Logger "Command failed." "ERROR"
fi fi
if [ -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ] && ([ $_VERBOSE -eq 1 ] || [ $retval -ne 0 ]) if [ -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ] && ([ $_VERBOSE == true ] || [ $retval -ne 0 ])
then then
Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "NOTICE" Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "NOTICE"
fi fi
@ -982,7 +1074,7 @@ function CheckConnectivity3rdPartyHosts {
if [ "$_PARANOIA_DEBUG" != "yes" ]; then # Do not loose time in paranoia debug if [ "$_PARANOIA_DEBUG" != "yes" ]; then # Do not loose time in paranoia debug
if [ "$REMOTE_3RD_PARTY_HOSTS" != "" ]; then if [ "$REMOTE_3RD_PARTY_HOSTS" != "" ]; then
remote_3rd_party_success=0 remote_3rd_party_success=false
for i in $REMOTE_3RD_PARTY_HOSTS for i in $REMOTE_3RD_PARTY_HOSTS
do do
eval "$PING_CMD $i > /dev/null 2>&1" & eval "$PING_CMD $i > /dev/null 2>&1" &
@ -990,11 +1082,11 @@ function CheckConnectivity3rdPartyHosts {
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot ping 3rd party host $i" "NOTICE" Logger "Cannot ping 3rd party host $i" "NOTICE"
else else
remote_3rd_party_success=1 remote_3rd_party_success=true
fi fi
done done
if [ $remote_3rd_party_success -ne 1 ]; then if [ $remote_3rd_party_success == false ]; then
Logger "No remote 3rd party host responded to ping. No internet ?" "ERROR" Logger "No remote 3rd party host responded to ping. No internet ?" "ERROR"
return 1 return 1
else else
@ -1121,7 +1213,7 @@ function PreInit {
## Set rsync default arguments ## Set rsync default arguments
RSYNC_ARGS="-rltD" RSYNC_ARGS="-rltD"
if [ "$_DRYRUN" -eq 1 ]; then if [ "$_DRYRUN" == true ]; then
RSYNC_DRY_ARG="-n" RSYNC_DRY_ARG="-n"
DRY_WARNING="/!\ DRY RUN" DRY_WARNING="/!\ DRY RUN"
else else
@ -1284,8 +1376,8 @@ PARTIAL_DIR=".obackup_workdir_partial"
# $FILE_SIZE_LIST_LOCAL, list of all directories to include in GetDirectoriesSize, enclosed by escaped doublequotes for local command # $FILE_SIZE_LIST_LOCAL, list of all directories to include in GetDirectoriesSize, enclosed by escaped doublequotes for local command
# $FILE_SIZE_LIST_REMOTE, list of all directories to include in GetDirectoriesSize, enclosed by escaped singlequotes for remote command # $FILE_SIZE_LIST_REMOTE, list of all directories to include in GetDirectoriesSize, enclosed by escaped singlequotes for remote command
CAN_BACKUP_SQL=1 CAN_BACKUP_SQL=true
CAN_BACKUP_FILES=1 CAN_BACKUP_FILES=true
function TrapStop { function TrapStop {
Logger "/!\ Manual exit of backup script. Backups may be in inconsistent state." "WARN" Logger "/!\ Manual exit of backup script. Backups may be in inconsistent state." "WARN"
@ -1295,7 +1387,7 @@ function TrapStop {
function TrapQuit { function TrapQuit {
local exitcode local exitcode
if [ $ERROR_ALERT -ne 0 ]; then if [ $ERROR_ALERT == true ]; then
if [ "$RUN_AFTER_CMD_ON_ERROR" == "yes" ]; then if [ "$RUN_AFTER_CMD_ON_ERROR" == "yes" ]; then
RunAfterHook RunAfterHook
fi fi
@ -1303,7 +1395,7 @@ function TrapQuit {
Logger "Backup script finished with errors." "ERROR" Logger "Backup script finished with errors." "ERROR"
SendAlert SendAlert
exitcode=1 exitcode=1
elif [ $WARN_ALERT -ne 0 ]; then elif [ $WARN_ALERT == true ]; then
if [ "$RUN_AFTER_CMD_ON_ERROR" == "yes" ]; then if [ "$RUN_AFTER_CMD_ON_ERROR" == "yes" ]; then
RunAfterHook RunAfterHook
fi fi
@ -1337,11 +1429,11 @@ function CheckEnvironment {
if [ "$SQL_BACKUP" != "no" ]; then if [ "$SQL_BACKUP" != "no" ]; then
if ! type mysqldump > /dev/null 2>&1 ; then if ! type mysqldump > /dev/null 2>&1 ; then
Logger "mysqldump not present. Cannot backup SQL." "CRITICAL" Logger "mysqldump not present. Cannot backup SQL." "CRITICAL"
CAN_BACKUP_SQL=0 CAN_BACKUP_SQL=false
fi fi
if ! type mysql > /dev/null 2>&1 ; then if ! type mysql > /dev/null 2>&1 ; then
Logger "mysql not present. Cannot backup SQL." "CRITICAL" Logger "mysql not present. Cannot backup SQL." "CRITICAL"
CAN_BACKUP_SQL=0 CAN_BACKUP_SQL=false
fi fi
fi fi
fi fi
@ -1350,12 +1442,12 @@ function CheckEnvironment {
if [ "$ENCRYPTION" == "yes" ]; then if [ "$ENCRYPTION" == "yes" ]; then
if ! type gpg > /dev/null 2>&1 ; then if ! type gpg > /dev/null 2>&1 ; then
Logger "gpg not present. Cannot encrypt backup files." "CRITICAL" Logger "gpg not present. Cannot encrypt backup files." "CRITICAL"
CAN_BACKUP_FILES=0 CAN_BACKUP_FILES=false
fi fi
else else
if ! type rsync > /dev/null 2>&1 ; then if ! type rsync > /dev/null 2>&1 ; then
Logger "rsync not present. Cannot backup files." "CRITICAL" Logger "rsync not present. Cannot backup files." "CRITICAL"
CAN_BACKUP_FILES=0 CAN_BACKUP_FILES=false
fi fi
fi fi
fi fi
@ -1390,11 +1482,11 @@ function CheckCurrentConfig {
if [ "$FILE_BACKUP" == "yes" ]; then if [ "$FILE_BACKUP" == "yes" ]; then
if [ "$DIRECTORY_LIST" == "" ] && [ "$RECURSIVE_DIRECTORY_LIST" == "" ]; then if [ "$DIRECTORY_LIST" == "" ] && [ "$RECURSIVE_DIRECTORY_LIST" == "" ]; then
Logger "No directories specified in config file, no files to backup." "ERROR" Logger "No directories specified in config file, no files to backup." "ERROR"
CAN_BACKUP_FILES=0 CAN_BACKUP_FILES=false
fi fi
fi fi
#TODO-v2.1: Add runtime variable tests (RSYNC_ARGS etc) #TODO-v2.1(ongoing WIP): Add runtime variable tests (RSYNC_ARGS etc)
if [ "$REMOTE_OPERATION" == "yes" ] && [ ! -f "$SSH_RSA_PRIVATE_KEY" ]; then if [ "$REMOTE_OPERATION" == "yes" ] && [ ! -f "$SSH_RSA_PRIVATE_KEY" ]; then
Logger "Cannot find rsa private key [$SSH_RSA_PRIVATE_KEY]. Cannot connect to remote system." "CRITICAL" Logger "Cannot find rsa private key [$SSH_RSA_PRIVATE_KEY]. Cannot connect to remote system." "CRITICAL"
exit 1 exit 1
@ -1469,7 +1561,7 @@ function ListDatabases {
local dbArray local dbArray
if [ $CAN_BACKUP_SQL -ne 1 ]; then if [ $CAN_BACKUP_SQL == false ]; then
Logger "Cannot list databases." "ERROR" Logger "Cannot list databases." "ERROR"
return 1 return 1
fi fi
@ -1492,7 +1584,7 @@ function ListDatabases {
fi fi
fi fi
if [ -f "$outputFile" ] && [ $CAN_BACKUP_SQL -eq 1 ]; then if [ -f "$outputFile" ] && [ $CAN_BACKUP_SQL == true ]; then
while read -r line; do while read -r line; do
while read -r name size; do dbName=$name; dbSize=$size; done <<< "$line" while read -r name size; do dbName=$name; dbSize=$size; done <<< "$line"
#db_name="${line% *}" #db_name="${line% *}"
@ -1534,7 +1626,7 @@ function ListDatabases {
Logger "Database exclude list: $SQL_EXCLUDED_TASKS" "DEBUG" Logger "Database exclude list: $SQL_EXCLUDED_TASKS" "DEBUG"
else else
Logger "Will not execute database backup." "ERROR" Logger "Will not execute database backup." "ERROR"
CAN_BACKUP_SQL=0 CAN_BACKUP_SQL=false
fi fi
} }
@ -1577,6 +1669,7 @@ function _ListRecursiveBackupDirectoriesRemote {
IFS=$PATH_SEPARATOR_CHAR read -r -a directories <<< "$RECURSIVE_DIRECTORY_LIST" IFS=$PATH_SEPARATOR_CHAR read -r -a directories <<< "$RECURSIVE_DIRECTORY_LIST"
for directory in "${directories[@]}"; do for directory in "${directories[@]}"; do
#TODO(med): Uses local home directory for remote lookup...
cmd=$SSH_CMD' "'$COMMAND_SUDO' '$REMOTE_FIND_CMD' -L '$directory'/ -mindepth 1 -maxdepth 1 -type d" >> '$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID' 2> '$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID cmd=$SSH_CMD' "'$COMMAND_SUDO' '$REMOTE_FIND_CMD' -L '$directory'/ -mindepth 1 -maxdepth 1 -type d" >> '$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID' 2> '$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID
Logger "cmd: $cmd" "DEBUG" Logger "cmd: $cmd" "DEBUG"
eval "$cmd" & eval "$cmd" &
@ -1602,6 +1695,8 @@ function ListRecursiveBackupDirectories {
local output_file local output_file
local file_exclude local file_exclude
local excluded
local fileArray local fileArray
Logger "Listing directories to backup." "NOTICE" Logger "Listing directories to backup." "NOTICE"
@ -1625,8 +1720,8 @@ function ListRecursiveBackupDirectories {
while read -r line; do while read -r line; do
file_exclude=0 file_exclude=0
IFS=$PATH_SEPARATOR_CHAR read -r -a fileArray <<< "$RECURSIVE_EXCLUDE_LIST" IFS=$PATH_SEPARATOR_CHAR read -r -a fileArray <<< "$RECURSIVE_EXCLUDE_LIST"
for k in "${fileArray[@]}"; do for excluded in "${fileArray[@]}"; do
if [ "$k" == "$line" ]; then if [ "$excluded" == "$line" ]; then
file_exclude=1 file_exclude=1
fi fi
done done
@ -1780,26 +1875,26 @@ function CreateStorageDirectories {
if [ "$SQL_BACKUP" != "no" ]; then if [ "$SQL_BACKUP" != "no" ]; then
_CreateDirectoryLocal "$SQL_STORAGE" _CreateDirectoryLocal "$SQL_STORAGE"
if [ $? != 0 ]; then if [ $? != 0 ]; then
CAN_BACKUP_SQL=0 CAN_BACKUP_SQL=false
fi fi
fi fi
if [ "$FILE_BACKUP" != "no" ]; then if [ "$FILE_BACKUP" != "no" ]; then
_CreateDirectoryLocal "$FILE_STORAGE" _CreateDirectoryLocal "$FILE_STORAGE"
if [ $? != 0 ]; then if [ $? != 0 ]; then
CAN_BACKUP_FILES=0 CAN_BACKUP_FILES=false
fi fi
fi fi
elif [ "$BACKUP_TYPE" == "push" ]; then elif [ "$BACKUP_TYPE" == "push" ]; then
if [ "$SQL_BACKUP" != "no" ]; then if [ "$SQL_BACKUP" != "no" ]; then
_CreateDirectoryRemote "$SQL_STORAGE" _CreateDirectoryRemote "$SQL_STORAGE"
if [ $? != 0 ]; then if [ $? != 0 ]; then
CAN_BACKUP_SQL=0 CAN_BACKUP_SQL=false
fi fi
fi fi
if [ "$FILE_BACKUP" != "no" ]; then if [ "$FILE_BACKUP" != "no" ]; then
_CreateDirectoryRemote "$FILE_STORAGE" _CreateDirectoryRemote "$FILE_STORAGE"
if [ $? != 0 ]; then if [ $? != 0 ]; then
CAN_BACKUP_FILES=0 CAN_BACKUP_FILES=false
fi fi
fi fi
fi fi
@ -1858,7 +1953,7 @@ function CheckDiskSpace {
GetDiskSpaceLocal "$SQL_STORAGE" GetDiskSpaceLocal "$SQL_STORAGE"
if [ $? != 0 ]; then if [ $? != 0 ]; then
SQL_DISK_SPACE=0 SQL_DISK_SPACE=0
CAN_BACKUP_SQL=0 CAN_BACKUP_SQL=false
else else
SQL_DISK_SPACE=$DISK_SPACE SQL_DISK_SPACE=$DISK_SPACE
SQL_DRIVE=$DRIVE SQL_DRIVE=$DRIVE
@ -1868,7 +1963,7 @@ function CheckDiskSpace {
GetDiskSpaceLocal "$FILE_STORAGE" GetDiskSpaceLocal "$FILE_STORAGE"
if [ $? != 0 ]; then if [ $? != 0 ]; then
FILE_DISK_SPACE=0 FILE_DISK_SPACE=0
CAN_BACKUP_FILES=0 CAN_BACKUP_FILES=false
else else
FILE_DISK_SPACE=$DISK_SPACE FILE_DISK_SPACE=$DISK_SPACE
FILE_DRIVE=$DRIVE FILE_DRIVE=$DRIVE
@ -1902,7 +1997,7 @@ function CheckDiskSpace {
TOTAL_FILES_SIZE=-1 TOTAL_FILES_SIZE=-1
fi fi
if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL -eq 1 ]; then if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL == true ]; then
if [ $SQL_DISK_SPACE -eq 0 ]; then if [ $SQL_DISK_SPACE -eq 0 ]; then
Logger "Storage space in [$SQL_STORAGE] reported to be 0Ko." "WARN" Logger "Storage space in [$SQL_STORAGE] reported to be 0Ko." "WARN"
fi fi
@ -1915,7 +2010,7 @@ function CheckDiskSpace {
Logger "SQL storage Space: $SQL_DISK_SPACE Ko - Databases size: $TOTAL_DATABASES_SIZE Ko" "NOTICE" Logger "SQL storage Space: $SQL_DISK_SPACE Ko - Databases size: $TOTAL_DATABASES_SIZE Ko" "NOTICE"
fi fi
if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES -eq 1 ]; then if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES == true ]; then
if [ $FILE_DISK_SPACE -eq 0 ]; then if [ $FILE_DISK_SPACE -eq 0 ]; then
Logger "Storage space in [$FILE_STORAGE] reported to be 0 Ko." "WARN" Logger "Storage space in [$FILE_STORAGE] reported to be 0 Ko." "WARN"
fi fi
@ -1945,7 +2040,7 @@ function _BackupDatabaseLocalToLocal {
local dry_sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS > /dev/null 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID" local dry_sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS > /dev/null 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID"
local sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS > $SQL_STORAGE/$database.sql$COMPRESSION_EXTENSION 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID" local sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS > $SQL_STORAGE/$database.sql$COMPRESSION_EXTENSION 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID"
if [ $_DRYRUN -ne 1 ]; then if [ $_DRYRUN == false ]; then
Logger "cmd: $sql_cmd" "DEBUG" Logger "cmd: $sql_cmd" "DEBUG"
eval "$sql_cmd" & eval "$sql_cmd" &
else else
@ -1977,7 +2072,7 @@ function _BackupDatabaseLocalToRemote {
local dry_sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS > /dev/null 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID" local dry_sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS > /dev/null 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID"
local sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS | $SSH_CMD '$COMMAND_SUDO tee \"$SQL_STORAGE/$database.sql$COMPRESSION_EXTENSION\" > /dev/null' 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID" local sql_cmd="mysqldump -u $SQL_USER $export_options --database $database $COMPRESSION_PROGRAM $COMPRESSION_OPTIONS | $SSH_CMD '$COMMAND_SUDO tee \"$SQL_STORAGE/$database.sql$COMPRESSION_EXTENSION\" > /dev/null' 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID"
if [ $_DRYRUN -ne 1 ]; then if [ $_DRYRUN == false ]; then
Logger "cmd: $sql_cmd" "DEBUG" Logger "cmd: $sql_cmd" "DEBUG"
eval "$sql_cmd" & eval "$sql_cmd" &
else else
@ -2009,7 +2104,7 @@ function _BackupDatabaseRemoteToLocal {
local dry_sql_cmd=$SSH_CMD' "mysqldump -u '$SQL_USER' '$export_options' --database '$database' '$COMPRESSION_PROGRAM' '$COMPRESSION_OPTIONS'" > /dev/null 2> "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID'"' local dry_sql_cmd=$SSH_CMD' "mysqldump -u '$SQL_USER' '$export_options' --database '$database' '$COMPRESSION_PROGRAM' '$COMPRESSION_OPTIONS'" > /dev/null 2> "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID'"'
local sql_cmd=$SSH_CMD' "mysqldump -u '$SQL_USER' '$export_options' --database '$database' '$COMPRESSION_PROGRAM' '$COMPRESSION_OPTIONS'" > "'$SQL_STORAGE/$database.sql$COMPRESSION_EXTENSION'" 2> "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID'"' local sql_cmd=$SSH_CMD' "mysqldump -u '$SQL_USER' '$export_options' --database '$database' '$COMPRESSION_PROGRAM' '$COMPRESSION_OPTIONS'" > "'$SQL_STORAGE/$database.sql$COMPRESSION_EXTENSION'" 2> "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID'"'
if [ $_DRYRUN -ne 1 ]; then if [ $_DRYRUN == false ]; then
Logger "cmd: $sql_cmd" "DEBUG" Logger "cmd: $sql_cmd" "DEBUG"
eval "$sql_cmd" & eval "$sql_cmd" &
else else
@ -2033,9 +2128,9 @@ function BackupDatabase {
# Hack to prevent warning on table mysql.events, some mysql versions don't support --skip-events, prefer using --ignore-table # Hack to prevent warning on table mysql.events, some mysql versions don't support --skip-events, prefer using --ignore-table
if [ "$database" == "mysql" ]; then if [ "$database" == "mysql" ]; then
mysql_options='--skip-lock-tables --single-transaction --ignore-table=mysql.event' mysql_options="$MYSQLDUMP_OPTIONS --ignore-table=mysql.event"
else else
mysql_options='--skip-lock-tables --single-transaction' mysql_options="$MYSQLDUMP_OPTIONS"
fi fi
if [ "$BACKUP_TYPE" == "local" ]; then if [ "$BACKUP_TYPE" == "local" ]; then
@ -2083,8 +2178,7 @@ function EncrpytFiles {
local tmpPath="${2}" local tmpPath="${2}"
#WIP: template code to split into local & remote code #gpg here ?
#crypt_cmd source temp #crypt_cmd source temp
# Send files to remote, rotate & copy # Send files to remote, rotate & copy
} }
@ -2114,19 +2208,19 @@ function Rsync {
# Creating subdirectories because rsync cannot handle multiple subdirectory creation # Creating subdirectories because rsync cannot handle multiple subdirectory creation
if [ "$BACKUP_TYPE" == "local" ]; then if [ "$BACKUP_TYPE" == "local" ]; then
_CreateDirectoryLocal "$file_storage_path" _CreateDirectoryLocal "$file_storage_path"
rsync_cmd="$(type -p $RSYNC_EXECUTABLE) $RSYNC_ARGS $RSYNC_DRY_ARG $RSYNC_ATTR_ARGS $RSYNC_TYPE_ARGS $RSYNC_NO_RECURSE_ARGS --stats $RSYNC_DELETE $RSYNC_PATTERNS $RSYNC_PARTIAL_EXCLUDE --rsync-path=\"$RSYNC_PATH\" \"$backup_directory\" \"$file_storage_path\" > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1" rsync_cmd="$(type -p $RSYNC_EXECUTABLE) $RSYNC_ARGS $RSYNC_DRY_ARG $RSYNC_ATTR_ARGS $RSYNC_TYPE_ARGS $RSYNC_NO_RECURSE_ARGS $RSYNC_DELETE $RSYNC_PATTERNS $RSYNC_PARTIAL_EXCLUDE --rsync-path=\"$RSYNC_PATH\" \"$backup_directory\" \"$file_storage_path\" > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1"
elif [ "$BACKUP_TYPE" == "pull" ]; then elif [ "$BACKUP_TYPE" == "pull" ]; then
_CreateDirectoryLocal "$file_storage_path" _CreateDirectoryLocal "$file_storage_path"
CheckConnectivity3rdPartyHosts CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost CheckConnectivityRemoteHost
backup_directory=$(EscapeSpaces "$backup_directory") backup_directory=$(EscapeSpaces "$backup_directory")
rsync_cmd="$(type -p $RSYNC_EXECUTABLE) $RSYNC_ARGS $RSYNC_DRY_ARG $RSYNC_ATTR_ARGS $RSYNC_TYPE_ARGS $RSYNC_NO_RECURSE_ARGS --stats $RSYNC_DELETE $RSYNC_PATTERNS $RSYNC_PARTIAL_EXCLUDE --rsync-path=\"$RSYNC_PATH\" -e \"$RSYNC_SSH_CMD\" \"$REMOTE_USER@$REMOTE_HOST:$backup_directory\" \"$file_storage_path\" > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1" rsync_cmd="$(type -p $RSYNC_EXECUTABLE) $RSYNC_ARGS $RSYNC_DRY_ARG $RSYNC_ATTR_ARGS $RSYNC_TYPE_ARGS $RSYNC_NO_RECURSE_ARGS $RSYNC_DELETE $RSYNC_PATTERNS $RSYNC_PARTIAL_EXCLUDE --rsync-path=\"$RSYNC_PATH\" -e \"$RSYNC_SSH_CMD\" \"$REMOTE_USER@$REMOTE_HOST:$backup_directory\" \"$file_storage_path\" > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1"
elif [ "$BACKUP_TYPE" == "push" ]; then elif [ "$BACKUP_TYPE" == "push" ]; then
CheckConnectivity3rdPartyHosts CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost CheckConnectivityRemoteHost
file_storage_path=$(EscapeSpaces "$file_storage_path") file_storage_path=$(EscapeSpaces "$file_storage_path")
_CreateDirectoryRemote "$file_storage_path" _CreateDirectoryRemote "$file_storage_path"
rsync_cmd="$(type -p $RSYNC_EXECUTABLE) $RSYNC_ARGS $RSYNC_DRY_ARG $RSYNC_ATTR_ARGS $RSYNC_TYPE_ARGS $RSYNC_NO_RECURSE_ARGS --stats $RSYNC_DELETE $RSYNC_PATTERNS $RSYNC_PARTIAL_EXCLUDE --rsync-path=\"$RSYNC_PATH\" -e \"$RSYNC_SSH_CMD\" \"$backup_directory\" \"$REMOTE_USER@$REMOTE_HOST:$file_storage_path\" > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1" rsync_cmd="$(type -p $RSYNC_EXECUTABLE) $RSYNC_ARGS $RSYNC_DRY_ARG $RSYNC_ATTR_ARGS $RSYNC_TYPE_ARGS $RSYNC_NO_RECURSE_ARGS $RSYNC_DELETE $RSYNC_PATTERNS $RSYNC_PARTIAL_EXCLUDE --rsync-path=\"$RSYNC_PATH\" -e \"$RSYNC_SSH_CMD\" \"$backup_directory\" \"$REMOTE_USER@$REMOTE_HOST:$file_storage_path\" > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1"
fi fi
Logger "cmd: $rsync_cmd" "DEBUG" Logger "cmd: $rsync_cmd" "DEBUG"
@ -2140,47 +2234,6 @@ function Rsync {
fi fi
} }
function Duplicity {
local backup_directory="${1}" # Which directory to backup
local is_recursive="${2}" # Backup only files at toplevel of directory
local file_storage_path
local duplicity_cmd
Logger "Encrpytion not supported yet ! No backup done." "CRITICAL"
return 1
if [ "$KEEP_ABSOLUTE_PATHS" == "yes" ]; then
file_storage_path="$(dirname $FILE_STORAGE$backup_directory)"
else
file_storage_path="$FILE_STORAGE"
fi
if [ "$BACKUP_TYPE" == "local" ]; then
duplicity_cmd=""
elif [ "$BACKUP_TYPE" == "pull" ]; then
CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost
duplicity_cmd=""
elif [ "$BACKUP_TYPE" == "push" ]; then
CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost
duplicity_cmd=""
fi
Logger "cmd: $duplicity_cmd" "DEBUG"
eval "$duplicity_cmd" &
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_FILE_TASK $HARD_MAX_EXEC_TIME_FILE_TASK ${FUNCNAME[0]} true $KEEP_LOGGING
if [ $? != 0 ]; then
Logger "Failed to backup [$backup_directory] to [$file_storage_path]." "ERROR"
Logger "Command output:\n $(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
else
Logger "File backup succeed." "NOTICE"
fi
}
function FilesBackup { function FilesBackup {
local backupTask local backupTask
@ -2190,7 +2243,9 @@ function FilesBackup {
for backupTask in "${backupTasks[@]}"; do for backupTask in "${backupTasks[@]}"; do
Logger "Beginning file backup of [$backupTask]." "NOTICE" Logger "Beginning file backup of [$backupTask]." "NOTICE"
if [ "$ENCRYPTION" == "yes" ]; then if [ "$ENCRYPTION" == "yes" ]; then
Duplicity "$backupTask" "recurse" Dummy
#Encrpyt files recursively
#Rsync encrypted files instead of original ones
else else
Rsync "$backupTask" "recurse" Rsync "$backupTask" "recurse"
fi fi
@ -2201,7 +2256,9 @@ function FilesBackup {
for backupTask in "${backupTasks[@]}"; do for backupTask in "${backupTasks[@]}"; do
Logger "Beginning non recursive file backup of [$backupTask]." "NOTICE" Logger "Beginning non recursive file backup of [$backupTask]." "NOTICE"
if [ "$ENCRYPTION" == "yes" ]; then if [ "$ENCRYPTION" == "yes" ]; then
Duplicity "$backupTask" "no-recurse" Dummy
#Encrpyt files non recursively
#Rsync encrypted files instead of original ones
else else
Rsync "$backupTask" "no-recurse" Rsync "$backupTask" "no-recurse"
fi fi
@ -2213,7 +2270,9 @@ function FilesBackup {
# Backup sub directories of recursive directories # Backup sub directories of recursive directories
Logger "Beginning recursive file backup of [$backupTask]." "NOTICE" Logger "Beginning recursive file backup of [$backupTask]." "NOTICE"
if [ "$ENCRYPTION" == "yes" ]; then if [ "$ENCRYPTION" == "yes" ]; then
Duplicity "$backupTask" "recurse" Dummy
#Encrpyt files recursively
#Rsync encrypted files instead of original ones
else else
Rsync "$backupTask" "recurse" Rsync "$backupTask" "recurse"
fi fi
@ -2471,7 +2530,7 @@ function Init {
## Add update to default RSYNC_ARGS ## Add update to default RSYNC_ARGS
RSYNC_ARGS=$RSYNC_ARGS" -u" RSYNC_ARGS=$RSYNC_ARGS" -u"
if [ $_VERBOSE -eq 1 ]; then if [ $_VERBOSE == true ]; then
RSYNC_ARGS=$RSYNC_ARGS" -i" RSYNC_ARGS=$RSYNC_ARGS" -i"
fi fi
@ -2479,7 +2538,7 @@ function Init {
RSYNC_ARGS=$RSYNC_ARGS" --delete" RSYNC_ARGS=$RSYNC_ARGS" --delete"
fi fi
if [ $stats -eq 1 ]; then if [ $stats == true ]; then
RSYNC_ARGS=$RSYNC_ARGS" --stats" RSYNC_ARGS=$RSYNC_ARGS" --stats"
fi fi
@ -2489,10 +2548,10 @@ function Init {
function Main { function Main {
if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL -eq 1 ]; then if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL == true ]; then
ListDatabases ListDatabases
fi fi
if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES -eq 1 ]; then if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES == true ]; then
ListRecursiveBackupDirectories ListRecursiveBackupDirectories
if [ "$GET_BACKUP_SIZE" != "no" ]; then if [ "$GET_BACKUP_SIZE" != "no" ]; then
GetDirectoriesSize GetDirectoriesSize
@ -2501,21 +2560,27 @@ function Main {
fi fi
fi fi
# Expand ~ if exists
FILE_STORAGE="${FILE_STORAGE/#\~/$HOME}"
SQL_STORAGE="${SQL_STORAGE/#\~/$HOME}"
SSH_RSA_PRIVATE_KEY="${SSH_RSA_PRIVATE_KEY/#\~/$HOME}"
ENCRYPT_PUBKEY="${ENCRPYT_PUBKEY/#\~/$HOME}"
if [ "$CREATE_DIRS" != "no" ]; then if [ "$CREATE_DIRS" != "no" ]; then
CreateStorageDirectories CreateStorageDirectories
fi fi
CheckDiskSpace CheckDiskSpace
# Actual backup process # Actual backup process
if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL -eq 1 ]; then if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL == true ]; then
if [ $_DRYRUN -ne 1 ] && [ "$ROTATE_SQL_BACKUPS" == "yes" ]; then if [ $_DRYRUN == false ] && [ "$ROTATE_SQL_BACKUPS" == "yes" ]; then
RotateBackups "$SQL_STORAGE" "$ROTATE_SQL_COPIES" RotateBackups "$SQL_STORAGE" "$ROTATE_SQL_COPIES"
fi fi
BackupDatabases BackupDatabases
fi fi
if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES -eq 1 ]; then if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES == true ]; then
if [ $_DRYRUN -ne 1 ] && [ "$ROTATE_FILE_BACKUPS" == "yes" ]; then if [ $_DRYRUN == false ] && [ "$ROTATE_FILE_BACKUPS" == "yes" ]; then
RotateBackups "$FILE_STORAGE" "$ROTATE_FILE_COPIES" RotateBackups "$FILE_STORAGE" "$ROTATE_FILE_COPIES"
fi fi
## Add Rsync include / exclude patterns ## Add Rsync include / exclude patterns
@ -2550,11 +2615,11 @@ function Usage {
} }
# Command line argument flags # Command line argument flags
_DRYRUN=0 _DRYRUN=false
_SILENT=0 _SILENT=false
no_maxtime=0 no_maxtime=false
stats=0 stats=false
PARTIAL=0 PARTIAL=no
function GetCommandlineArguments { function GetCommandlineArguments {
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
@ -2564,22 +2629,22 @@ function GetCommandlineArguments {
for i in "$@"; do for i in "$@"; do
case $i in case $i in
--dry) --dry)
_DRYRUN=1 _DRYRUN=true
;; ;;
--silent) --silent)
_SILENT=1 _SILENT=true
;; ;;
--verbose) --verbose)
_VERBOSE=1 _VERBOSE=true
;; ;;
--stats) --stats)
stats=1 stats=false
;; ;;
--partial) --partial)
PARTIAL="yes" PARTIAL="yes"
;; ;;
--no-maxtime) --no-maxtime)
no_maxtime=1 no_maxtime=true
;; ;;
--delete) --delete)
DELETE_VANISHED_FILES="yes" DELETE_VANISHED_FILES="yes"
@ -2599,8 +2664,8 @@ LoadConfigFile "$1"
if [ "$LOGFILE" == "" ]; then if [ "$LOGFILE" == "" ]; then
if [ -w /var/log ]; then if [ -w /var/log ]; then
LOG_FILE="/var/log/$PROGRAM.$INSTANCE_ID.log" LOG_FILE="/var/log/$PROGRAM.$INSTANCE_ID.log"
elif ([ "$HOME" != "" ] && [ -w "$HOME" ]); then elif ([ "${HOME}" != "" ] && [ -w "{$HOME}" ]); then
LOG_FILE="$HOME/$PROGRAM.$INSTANCE_ID.log" LOG_FILE="${HOME}/$PROGRAM.$INSTANCE_ID.log"
else else
LOG_FILE=./$PROGRAM.$INSTANCE_ID.log LOG_FILE=./$PROGRAM.$INSTANCE_ID.log
fi fi
@ -2632,7 +2697,7 @@ if [ "$REMOTE_OPERATION" == "yes" ]; then
InitRemoteOSSettings InitRemoteOSSettings
fi fi
if [ $no_maxtime -eq 1 ]; then if [ $no_maxtime == true ]; then
SOFT_MAX_EXEC_TIME_DB_TASK=0 SOFT_MAX_EXEC_TIME_DB_TASK=0
SOFT_MAX_EXEC_TIME_FILE_TASK=0 SOFT_MAX_EXEC_TIME_FILE_TASK=0
HARD_MAX_EXEC_TIME_DB_TASK=0 HARD_MAX_EXEC_TIME_DB_TASK=0