obackup/obackup.sh

2270 lines
67 KiB
Bash
Raw Normal View History

2015-11-12 01:26:38 +01:00
#!/usr/bin/env bash
2013-07-16 23:10:27 +02:00
2015-11-12 01:26:38 +01:00
###### Remote push/pull (or local) backup script for files & databases
PROGRAM="obackup"
2016-02-19 22:14:13 +01:00
AUTHOR="(L) 2013-2016 by Orsiris de Jong"
CONTACT="http://www.netpower.fr/obackup - ozy@netpower.fr"
2015-11-12 01:26:38 +01:00
PROGRAM_VERSION=2.0-pre
PROGRAM_BUILD=2016031801
2015-11-12 01:26:38 +01:00
IS_STABLE=no
2016-03-20 16:49:29 +01:00
## FUNC_BUILD=2016032001
2016-02-19 22:14:13 +01:00
## BEGIN Generic functions for osync & obackup written in 2013-2016 by Orsiris de Jong - http://www.netpower.fr - ozy@netpower.fr
## 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
echo "Please run this script only with bash shell. Tested on bash >= 3.2"
exit 127
fi
#### obackup & osync specific code BEGIN ####
## Log a state message every $KEEP_LOGGING seconds. Should not be equal to soft or hard execution time so your log will not be unnecessary big.
KEEP_LOGGING=1801
## Correct output of sort command (language agnostic sorting)
export LC_ALL=C
# Standard alert mail body
MAIL_ALERT_MSG="Execution of $PROGRAM instance $INSTANCE_ID on $(date) has warnings/errors."
#### obackup & osync specific code END ####
#### MINIMAL-FUNCTION-SET BEGIN ####
# Environment variables
_DRYRUN=0
_SILENT=0
# Initial error status, logging 'WARN', 'ERROR' or 'CRITICAL' will enable alerts flags
ERROR_ALERT=0
WARN_ALERT=0
## allow debugging from command line with _DEBUG=yes
if [ ! "$_DEBUG" == "yes" ]; then
_DEBUG=no
SLEEP_TIME=.1
_VERBOSE=0
else
SLEEP_TIME=1
trap 'TrapError ${LINENO} $?' ERR
_VERBOSE=1
fi
SCRIPT_PID=$$
LOCAL_USER=$(whoami)
LOCAL_HOST=$(hostname)
## Default log file until config file is loaded
if [ -w /var/log ]; then
LOG_FILE="/var/log/$PROGRAM.log"
else
LOG_FILE="./$PROGRAM.log"
fi
## Default directory where to store temporary run files
if [ -w /tmp ]; then
RUN_DIR=/tmp
elif [ -w /var/tmp ]; then
RUN_DIR=/var/tmp
else
RUN_DIR=.
fi
# Default alert attachment filename
ALERT_LOG_FILE="$RUN_DIR/$PROGRAM.last.log"
# Set error exit code if a piped command fails
set -o pipefail
set -o errtrace
function Dummy {
sleep .1
}
function _Logger {
local svalue="${1}" # What to log to screen
local lvalue="${2:-$svalue}" # What to log to logfile, defaults to screen value
echo -e "$lvalue" >> "$LOG_FILE"
if [ $_SILENT -eq 0 ]; then
echo -e "$svalue"
fi
}
function Logger {
local value="${1}" # Sentence to log (in double quotes)
local level="${2}" # Log level: PARANOIA_DEBUG, DEBUG, NOTICE, WARN, ERROR, CRITIAL
# <OSYNC SPECIFIC> Special case in daemon mode we should timestamp instead of counting seconds
if [ "$sync_on_changes" == "1" ]; then
prefix="$(date) - "
else
prefix="TIME: $SECONDS - "
fi
# </OSYNC SPECIFIC>
if [ "$level" == "CRITICAL" ]; then
_Logger "$prefix\e[41m$value\e[0m" "$prefix$level:$value"
ERROR_ALERT=1
return
elif [ "$level" == "ERROR" ]; then
_Logger "$prefix\e[91m$value\e[0m" "$prefix$level:$value"
ERROR_ALERT=1
return
elif [ "$level" == "WARN" ]; then
_Logger "$prefix\e[93m$value\e[0m" "$prefix$level:$value"
WARN_ALERT=1
return
elif [ "$level" == "NOTICE" ]; then
_Logger "$prefix$value"
return
elif [ "$level" == "DEBUG" ]; then
if [ "$_DEBUG" == "yes" ]; then
_Logger "$prefix$value"
return
fi
else
_Logger "\e[41mLogger function called without proper loglevel.\e[0m"
_Logger "$prefix$value"
fi
}
# Portable child (and grandchild) kill function tester under Linux, BSD and MacOS X
function KillChilds {
local pid="${1}"
local self="${2:-false}"
if children="$(pgrep -P "$pid")"; then
for child in $children; do
KillChilds "$child" true
done
fi
# Try to kill nicely, if not, wait 30 seconds to let Trap actions happen before killing
if [ "$self" == true ]; then
kill -s SIGTERM "$pid" || (sleep 30 && kill -9 "$pid" &)
fi
2016-03-10 10:26:26 +01:00
# sleep 30 needs to wait before killing itself
}
function SendAlert {
2016-02-19 22:14:13 +01:00
local mail_no_attachment=
local attachment_command=
2016-03-10 10:26:26 +01:00
if [ "$DESTINATION_MAILS" == "" ]; then
return 0
fi
if [ "$_DEBUG" == "yes" ]; then
Logger "Debug mode, no warning email will be sent." "NOTICE"
return 0
fi
# <OSYNC SPECIFIC>
if [ "$_QUICK_SYNC" == "2" ]; then
Logger "Current task is a quicksync task. Will not send any alert." "NOTICE"
return 0
fi
# </OSYNC SPECIFIC>
eval "cat \"$LOG_FILE\" $COMPRESSION_PROGRAM > $ALERT_LOG_FILE"
2016-02-16 21:09:57 +01:00
if [ $? != 0 ]; then
Logger "Cannot create [$ALERT_LOG_FILE]" "WARN"
mail_no_attachment=1
else
mail_no_attachment=0
fi
2015-11-19 23:05:59 +01:00
MAIL_ALERT_MSG="$MAIL_ALERT_MSG"$'\n\n'$(tail -n 50 "$LOG_FILE")
if [ $ERROR_ALERT -eq 1 ]; then
subject="Error alert for $INSTANCE_ID"
elif [ $WARN_ALERT -eq 1 ]; then
subject="Warning alert for $INSTANCE_ID"
else
subject="Alert for $INSTANCE_ID"
fi
2016-02-19 22:14:13 +01:00
if [ "$mail_no_attachment" -eq 0 ]; then
2016-02-16 21:09:57 +01:00
attachment_command="-a $ALERT_LOG_FILE"
fi
if type mutt > /dev/null 2>&1 ; then
2016-03-03 16:33:59 +01:00
cmd="echo \"$MAIL_ALERT_MSG\" | $(type -p mutt) -x -s \"$subject\" $DESTINATION_MAILS $attachment_command"
Logger "Mail cmd: $cmd" "DEBUG"
2016-03-03 16:33:59 +01:00
eval $cmd
if [ $? != 0 ]; then
2016-03-03 16:33:59 +01:00
Logger "Cannot send alert email via $(type -p mutt) !!!" "WARN"
else
Logger "Sent alert mail using mutt." "NOTICE"
return 0
fi
fi
if type mail > /dev/null 2>&1 ; then
2016-02-19 22:14:13 +01:00
if [ "$mail_no_attachment" -eq 0 ] && $(type -p mail) -V | grep "GNU" > /dev/null; then
2016-02-16 21:09:57 +01:00
attachment_command="-A $ALERT_LOG_FILE"
2016-02-19 22:14:13 +01:00
elif [ "$mail_no_attachment" -eq 0 ] && $(type -p mail) -V > /dev/null; then
2016-02-16 21:09:57 +01:00
attachment_command="-a $ALERT_LOG_FILE"
else
attachment_command=""
fi
2016-03-03 16:33:59 +01:00
cmd="echo \"$MAIL_ALERT_MSG\" | $(type -p mail) $attachment_command -s \"$subject\" $DESTINATION_MAILS"
Logger "Mail cmd: $cmd" "DEBUG"
2016-03-03 16:33:59 +01:00
eval $cmd
if [ $? != 0 ]; then
2016-03-03 16:33:59 +01:00
Logger "Cannot send alert email via $(type -p mail) with attachments !!!" "WARN"
cmd="echo \"$MAIL_ALERT_MSG\" | $(type -p mail) -s \"$subject\" $DESTINATION_MAILS"
Logger "Mail cmd: $cmd" "DEBUG"
2016-03-03 16:33:59 +01:00
eval $cmd
if [ $? != 0 ]; then
2016-03-03 16:33:59 +01:00
Logger "Cannot send alert email via $(type -p mail) without attachments !!!" "WARN"
else
Logger "Sent alert mail using mail command without attachment." "NOTICE"
return 0
fi
else
Logger "Sent alert mail using mail command." "NOTICE"
return 0
fi
fi
if type sendmail > /dev/null 2>&1 ; then
2016-03-03 16:33:59 +01:00
cmd="echo -e \"Subject:$subject\r\n$MAIL_ALERT_MSG\" | $(type -p sendmail) $DESTINATION_MAILS"
Logger "Mail cmd: $cmd" "DEBUG"
2016-03-03 16:33:59 +01:00
eval $cmd
if [ $? != 0 ]; then
2016-03-03 16:33:59 +01:00
Logger "Cannot send alert email via $(type -p sendmail) !!!" "WARN"
else
Logger "Sent alert mail using sendmail command without attachment." "NOTICE"
return 0
fi
fi
if type sendemail > /dev/null 2>&1 ; then
if [ "$SMTP_USER" != "" ] && [ "$SMTP_PASSWORD" != "" ]; then
SMTP_OPTIONS="-xu $SMTP_USER -xp $SMTP_PASSWORD"
else
SMTP_OPTIONS=""
fi
$(type -p sendemail) -f $SENDER_MAIL -t $DESTINATION_MAILS -u "$subject" -m "$MAIL_ALERT_MSG" -s $SMTP_SERVER $SMTP_OPTIONS > /dev/null 2>&1
if [ $? != 0 ]; then
2016-03-03 16:33:59 +01:00
Logger "Cannot send alert email via $(type -p sendemail) !!!" "WARN"
else
Logger "Sent alert mail using sendemail command without attachment." "NOTICE"
return 0
fi
fi
# If function has not returned 0 yet, assume it's critical that no alert can be sent
2016-03-03 16:33:59 +01:00
Logger "Cannot send alert (neither mutt, mail, sendmail nor sendemail found)." "ERROR" # Is not marked critical because execution must continue
# Delete tmp log file
if [ -f "$ALERT_LOG_FILE" ]; then
rm "$ALERT_LOG_FILE"
fi
}
2016-03-04 09:34:02 +01:00
function TrapError {
local job="$0"
local line="$1"
local code="${2:-1}"
if [ $_SILENT -eq 0 ]; then
echo -e " /!\ ERROR in ${job}: Near line ${line}, exit code ${code}"
fi
}
function LoadConfigFile {
local config_file="${1}"
if [ ! -f "$config_file" ]; then
Logger "Cannot load configuration file [$config_file]. Cannot start." "CRITICAL"
exit 1
elif [[ "$1" != *".conf" ]]; then
Logger "Wrong configuration file supplied [$config_file]. Cannot start." "CRITICAL"
exit 1
else
grep '^[^ ]*=[^;&]*' "$config_file" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" # WITHOUT COMMENTS
# Shellcheck source=./sync.conf
source "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID"
fi
CONFIG_FILE="$config_file"
}
#### MINIMAL-FUNCTION-SET END ####
2016-03-04 09:34:02 +01:00
function Spinner {
if [ $_SILENT -eq 1 ]; then
return 0
fi
case $toggle
in
1)
echo -n " \ "
echo -ne "\r"
toggle="2"
;;
2)
echo -n " | "
echo -ne "\r"
toggle="3"
;;
3)
echo -n " / "
echo -ne "\r"
toggle="4"
;;
*)
echo -n " - "
echo -ne "\r"
toggle="1"
;;
esac
}
function SedStripQuotes {
echo $(echo $1 | sed "s/^\([\"']\)\(.*\)\1\$/\2/g")
}
function StripSingleQuotes {
local string="${1}"
string="${string/#\'/}" # Remove singlequote if it begins string
string="${string/%\'/}" # Remove singlequote if it ends string
echo "$string"
}
function StripDoubleQuotes {
local string="${1}"
string="${string/#\"/}"
string="${string/%\"/}"
echo "$string"
}
function StripQuotes {
local string="${1}"
echo "$(StripSingleQuotes $(StripDoubleQuotes $string))"
}
function EscapeSpaces {
local string="${1}" # String on which spaces will be escaped
echo "${string// /\ }"
}
function IsNumeric {
eval "local value=\"${1}\"" # Needed so variable variables can be processed
local re="^-?[0-9]+([.][0-9]+)?$"
if [[ $value =~ $re ]]; then
echo 1
else
echo 0
fi
}
function CleanUp {
if [ "$_DEBUG" != "yes" ]; then
rm -f "$RUN_DIR/$PROGRAM."*".$SCRIPT_PID"
fi
}
function GetLocalOS {
2016-02-19 22:14:13 +01:00
local local_os_var=
local_os_var="$(uname -spio 2>&1)"
if [ $? != 0 ]; then
2016-02-19 22:14:13 +01:00
local_os_var="$(uname -v 2>&1)"
if [ $? != 0 ]; then
2016-02-19 22:14:13 +01:00
local_os_var="$(uname)"
fi
fi
case $local_os_var in
*"Linux"*)
LOCAL_OS="Linux"
;;
*"BSD"*)
LOCAL_OS="BSD"
;;
2016-03-20 16:49:29 +01:00
*"MINGW32"*|*"CYGWIN"*)
LOCAL_OS="msys"
;;
*"Darwin"*)
LOCAL_OS="MacOSX"
;;
*)
2016-03-20 16:49:29 +01:00
if [ "$IGNORE_OS_TYPE" == "yes" ]; then #DOC: Undocumented option
Logger "Running on unknown local OS." "WARN"
return
fi
Logger "Running on >> $local_os_var << not supported. Please report to the author." "ERROR"
exit 1
;;
esac
Logger "Local OS: [$local_os_var]." "DEBUG"
}
function GetRemoteOS {
2016-02-19 22:14:13 +01:00
local cmd=
local remote_os_var=
if [ "$REMOTE_OPERATION" == "yes" ]; then
CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost
2016-02-19 22:14:13 +01:00
cmd=$SSH_CMD' "uname -spio" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1'
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! 120 240 ${FUNCNAME[0]}"-1"
retval=$?
if [ $retval != 0 ]; then
2016-02-19 22:14:13 +01:00
cmd=$SSH_CMD' "uname -v" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1'
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! 120 240 ${FUNCNAME[0]}"-2"
retval=$?
if [ $retval != 0 ]; then
2016-02-19 22:14:13 +01:00
cmd=$SSH_CMD' "uname" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1'
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! 120 240 ${FUNCNAME[0]}"-3"
retval=$?
if [ $retval != 0 ]; then
Logger "Cannot Get remote OS type." "ERROR"
fi
fi
fi
2016-02-19 22:14:13 +01:00
remote_os_var=$(cat "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID")
case $remote_os_var in
*"Linux"*)
REMOTE_OS="Linux"
;;
*"BSD"*)
REMOTE_OS="BSD"
;;
2016-03-20 16:49:29 +01:00
*"MINGW32"*|*"CYGWIN"*)
REMOTE_OS="msys"
;;
*"Darwin"*)
REMOTE_OS="MacOSX"
;;
*"ssh"*|*"SSH"*)
Logger "Cannot connect to remote system." "CRITICAL"
exit 1
;;
*)
2016-03-20 16:49:29 +01:00
if [ "$IGNORE_OS_VER" == "yes" ]; then #DOC: Undocumented option
Logger "Running on unknown remote OS." "WARN"
return
fi
Logger "Running on remote OS failed. Please report to the author if the OS is not supported." "CRITICAL"
Logger "Remote OS said:\n$remote_os_var" "CRITICAL"
exit 1
esac
Logger "Remote OS: [$remote_os_var]." "DEBUG"
fi
}
function WaitForTaskCompletion {
local pid="${1}" # pid to wait for
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 hard_max_time="${3}" # If program with pid $pid takes longer than $hard_max_time seconds, will stop execution, unless $hard_max_time equals 0.
local caller_name="${4}" # Who called this function
local soft_alert=0 # Does a soft alert need to be triggered, if yes, send an alert once
local log_ttime=0 # local time instance for comparaison
local seconds_begin=$SECONDS # Seconds since the beginning of the script
local exec_time=0 # Seconds since the beginning of this function
while eval "$PROCESS_TEST_CMD" > /dev/null
do
Spinner
exec_time=$(($SECONDS - $seconds_begin))
if [ $((($exec_time + 1) % $KEEP_LOGGING)) -eq 0 ]; then
if [ $log_ttime -ne $exec_time ]; then
log_ttime=$exec_time
Logger "Current task still running." "NOTICE"
fi
fi
if [ $exec_time -gt $soft_max_time ]; then
if [ $soft_alert -eq 0 ] && [ $soft_max_time -ne 0 ]; then
2016-03-04 09:34:02 +01:00
Logger "Max soft execution time exceeded for task [$caller_name]." "WARN"
soft_alert=1
SendAlert
fi
if [ $exec_time -gt $hard_max_time ] && [ $hard_max_time -ne 0 ]; then
2016-03-04 09:34:02 +01:00
Logger "Max hard execution time exceeded for task [$caller_name]. Stopping task execution." "ERROR"
kill -s SIGTERM $pid
if [ $? == 0 ]; then
Logger "Task stopped succesfully" "NOTICE"
else
Logger "Sending SIGTERM to proces failed. Trying the hard way." "ERROR"
sleep 5 && kill -9 $pid
if [ $? != 0 ]; then
Logger "Could not stop task." "ERROR"
fi
fi
return 1
fi
fi
sleep $SLEEP_TIME
done
wait $pid
local retval=$?
return $retval
}
function WaitForCompletion {
local pid="${1}" # pid to wait for
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 hard_max_time="${3}" # If program with pid $pid takes longer than $hard_max_time seconds, will stop execution, unless $hard_max_time equals 0.
local caller_name="${4}" # Who called this function
local soft_alert=0 # Does a soft alert need to be triggered, if yes, send an alert once
local log_ttime=0 # local time instance for comparaison
local seconds_begin=$SECONDS # Seconds since the beginning of the script
local exec_time=0 # Seconds since the beginning of this function
while eval "$PROCESS_TEST_CMD" > /dev/null
do
Spinner
if [ $((($SECONDS + 1) % $KEEP_LOGGING)) -eq 0 ]; then
if [ $log_time -ne $SECONDS ]; then
log_time=$SECONDS
Logger "Current task still running." "NOTICE"
fi
fi
if [ $SECONDS -gt $soft_max_time ]; then
if [ $soft_alert -eq 0 ] && [ $soft_max_time != 0 ]; then
Logger "Max soft execution time exceeded for script." "WARN"
soft_alert=1
SendAlert
fi
if [ $SECONDS -gt $hard_max_time ] && [ $hard_max_time != 0 ]; then
2016-03-10 10:26:26 +01:00
Logger "Max hard execution time exceeded for script in [$caller_name]. Stopping current task execution." "ERROR"
kill -s SIGTERM $pid
if [ $? == 0 ]; then
Logger "Task stopped succesfully" "NOTICE"
else
Logger "Sending SIGTERM to proces failed. Trying the hard way." "ERROR"
kill -9 $pid
if [ $? != 0 ]; then
Logger "Could not stop task." "ERROR"
fi
fi
return 1
fi
fi
sleep $SLEEP_TIME
done
wait $pid
retval=$?
return $retval
}
function RunLocalCommand {
local command="${1}" # Command to run
local hard_max_time="${2}" # Max time to wait for command to compleet
if [ $_DRYRUN -ne 0 ]; then
Logger "Dryrun: Local command [$command] not run." "NOTICE"
return 0
fi
Logger "Running command [$command] on local host." "NOTICE"
2016-02-19 22:14:13 +01:00
eval "$command" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" 2>&1 &
WaitForTaskCompletion $! 0 $hard_max_time ${FUNCNAME[0]}
retval=$?
if [ $retval -eq 0 ]; then
Logger "Command succeded." "NOTICE"
else
Logger "Command failed." "ERROR"
fi
if [ $_VERBOSE -eq 1 ] || [ $retval -ne 0 ]; then
2016-02-19 22:14:13 +01:00
Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "NOTICE"
fi
if [ "$STOP_ON_CMD_ERROR" == "yes" ] && [ $retval -ne 0 ]; then
Logger "Stopping on command execution error." "CRITICAL"
exit 1
fi
}
## Runs remote command $1 and waits for completition in $2 seconds
function RunRemoteCommand {
local command="${1}" # Command to run
local hard_max_time="${2}" # Max time to wait for command to compleet
CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost
if [ $_DRYRUN -ne 0 ]; then
Logger "Dryrun: Local command [$command] not run." "NOTICE"
return 0
fi
Logger "Running command [$command] on remote host." "NOTICE"
2016-02-19 22:14:13 +01:00
cmd=$SSH_CMD' "$command" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1'
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! 0 $hard_max_time ${FUNCNAME[0]}
retval=$?
if [ $retval -eq 0 ]; then
Logger "Command succeded." "NOTICE"
else
Logger "Command failed." "ERROR"
fi
2016-02-19 22:14:13 +01:00
if [ -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ] && ([ $_VERBOSE -eq 1 ] || [ $retval -ne 0 ])
then
2016-02-19 22:14:13 +01:00
Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "NOTICE"
fi
if [ "$STOP_ON_CMD_ERROR" == "yes" ] && [ $retval -ne 0 ]; then
Logger "Stopping on command execution error." "CRITICAL"
exit 1
fi
}
function RunBeforeHook {
if [ "$LOCAL_RUN_BEFORE_CMD" != "" ]; then
RunLocalCommand "$LOCAL_RUN_BEFORE_CMD" $MAX_EXEC_TIME_PER_CMD_BEFORE
fi
if [ "$REMOTE_RUN_BEFORE_CMD" != "" ]; then
RunRemoteCommand "$REMOTE_RUN_BEFORE_CMD" $MAX_EXEC_TIME_PER_CMD_BEFORE
fi
}
function RunAfterHook {
if [ "$LOCAL_RUN_AFTER_CMD" != "" ]; then
RunLocalCommand "$LOCAL_RUN_AFTER_CMD" $MAX_EXEC_TIME_PER_CMD_AFTER
fi
if [ "$REMOTE_RUN_AFTER_CMD" != "" ]; then
RunRemoteCommand "$REMOTE_RUN_AFTER_CMD" $MAX_EXEC_TIME_PER_CMD_AFTER
fi
}
function CheckConnectivityRemoteHost {
if [ "$_PARANOIA_DEBUG" != "yes" ]; then # Do not loose time in paranoia debug
if [ "$REMOTE_HOST_PING" != "no" ] && [ "$REMOTE_OPERATION" != "no" ]; then
eval "$PING_CMD $REMOTE_HOST > /dev/null 2>&1" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! 180 180 ${FUNCNAME[0]}
if [ $? != 0 ]; then
Logger "Cannot ping $REMOTE_HOST" "CRITICAL"
return 1
fi
fi
fi
}
function CheckConnectivity3rdPartyHosts {
if [ "$_PARANOIA_DEBUG" != "yes" ]; then # Do not loose time in paranoia debug
if [ "$REMOTE_3RD_PARTY_HOSTS" != "" ]; then
remote_3rd_party_success=0
OLD_IFS=$IFS
IFS=$' \t\n'
for i in $REMOTE_3RD_PARTY_HOSTS
do
eval "$PING_CMD $i > /dev/null 2>&1" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! 360 360 ${FUNCNAME[0]}
if [ $? != 0 ]; then
2016-03-10 10:26:26 +01:00
Logger "Cannot ping 3rd party host $i" "NOTICE"
else
remote_3rd_party_success=1
fi
done
IFS=$OLD_IFS
if [ $remote_3rd_party_success -ne 1 ]; then
2016-03-10 10:26:26 +01:00
Logger "No remote 3rd party host responded to ping. No internet ?" "ERROR"
return 1
fi
fi
fi
}
#__BEGIN_WITH_PARANOIA_DEBUG
#__END_WITH_PARANOIA_DEBUG
2016-03-01 19:35:40 +01:00
function RsyncPatternsAdd {
local pattern="${1}"
local pattern_type="${2}" # exclude or include
local rest=
# Disable globbing so wildcards from exclusions do not get expanded
set -f
rest="$pattern"
while [ -n "$rest" ]
do
# Take the string until first occurence until $PATH_SEPARATOR_CHAR
str=${rest%%;*}
# Handle the last case
if [ "$rest" = "${rest/$PATH_SEPARATOR_CHAR/}" ]; then
rest=
else
# Cut everything before the first occurence of $PATH_SEPARATOR_CHAR
rest=${rest#*$PATH_SEPARATOR_CHAR}
fi
if [ "$RSYNC_PATTERNS" == "" ]; then
RSYNC_PATTERNS="--"$pattern_type"=\"$str\""
else
RSYNC_PATTERNS="$RSYNC_PATTERNS --"$pattern_type"=\"$str\""
fi
done
set +f
}
function RsyncPatternsFromAdd {
local pattern_from="${1}"
local pattern_type="${2}"
local pattern_from=
## Check if the exclude list has a full path, and if not, add the config file path if there is one
if [ "$(basename $pattern_from)" == "$pattern_from" ]; then
pattern_from="$(dirname $CONFIG_FILE)/$pattern_from"
fi
if [ -e "$pattern_from" ]; then
RSYNC_PATTERNS="$RSYNC_PATTERNS --"$pattern_type"-from=\"$pattern_from\""
fi
}
function RsyncPatterns {
if [ "$RSYNC_PATTERN_FIRST" == "exclude" ]; then
RsyncPatternsAdd "$RSYNC_EXCLUDE_PATTERN" "exclude"
if [ "$RSYNC_EXCLUDE_FROM" != "" ]; then
RsyncPatternsFromAdd "$RSYNC_EXCLUDE_FROM" "exclude"
fi
RsyncPatternsAdd "$RSYNC_INCLUDE_PATTERN" "include"
if [ "$RSYNC_INCLUDE_FROM" != "" ]; then
RsyncPatternsFromAdd "$RSYNC_INCLUDE_FROM" "include"
fi
elif [ "$RSYNC_PATTERN_FIRST" == "include" ]; then
RsyncPatternsAdd "$RSYNC_INCLUDE_PATTERN" "include"
if [ "$RSYNC_INCLUDE_FROM" != "" ]; then
RsyncPatternsFromAdd "$RSYNC_INCLUDE_FROM" "include"
fi
RsyncPatternsAdd "$RSYNC_EXCLUDE_PATTERN" "exclude"
if [ "$RSYNC_EXCLUDE_FROM" != "" ]; then
RsyncPatternsFromAdd "$RSYNC_EXCLUDE_FROM" "exclude"
fi
else
Logger "Bogus RSYNC_PATTERN_FIRST value in config file. Will not use rsync patterns." "WARN"
fi
}
function PreInit {
## SSH compression
if [ "$SSH_COMPRESSION" != "no" ]; then
SSH_COMP=-C
else
SSH_COMP=
fi
## Support for older config files without RSYNC_EXECUTABLE option
if [ "$RSYNC_EXECUTABLE" == "" ]; then
RSYNC_EXECUTABLE=rsync
fi
## Sudo execution option
if [ "$SUDO_EXEC" == "yes" ]; then
if [ "$RSYNC_REMOTE_PATH" != "" ]; then
RSYNC_PATH="sudo $RSYNC_REMOTE_PATH/$RSYNC_EXECUTABLE"
else
RSYNC_PATH="sudo $RSYNC_EXECUTABLE"
fi
COMMAND_SUDO="sudo"
else
if [ "$RSYNC_REMOTE_PATH" != "" ]; then
RSYNC_PATH="$RSYNC_REMOTE_PATH/$RSYNC_EXECUTABLE"
else
RSYNC_PATH="$RSYNC_EXECUTABLE"
fi
COMMAND_SUDO=""
fi
## Set rsync default arguments
2016-02-16 21:09:57 +01:00
RSYNC_ARGS="-rltD"
RSYNC_ATTR_ARGS="-pgo"
2016-02-19 22:14:13 +01:00
if [ "$_DRYRUN" -eq 1 ]; then
RSYNC_DRY_ARG="-n"
DRY_WARNING="/!\ DRY RUN"
else
RSYNC_DRY_ARG=""
fi
if [ "$PRESERVE_ACL" == "yes" ]; then
2016-02-16 21:09:57 +01:00
RSYNC_ATTR_ARGS=$RSYNC_ATTR_ARGS" -A"
fi
if [ "$PRESERVE_XATTR" == "yes" ]; then
2016-02-16 21:09:57 +01:00
RSYNC_ATTR_ARGS=$RSYNC_ATTR_ARGS" -X"
fi
if [ "$RSYNC_COMPRESS" == "yes" ]; then
RSYNC_ARGS=$RSYNC_ARGS" -z"
fi
if [ "$COPY_SYMLINKS" == "yes" ]; then
RSYNC_ARGS=$RSYNC_ARGS" -L"
fi
if [ "$KEEP_DIRLINKS" == "yes" ]; then
RSYNC_ARGS=$RSYNC_ARGS" -K"
fi
if [ "$PRESERVE_HARDLINKS" == "yes" ]; then
RSYNC_ARGS=$RSYNC_ARGS" -H"
fi
if [ "$CHECKSUM" == "yes" ]; then
2016-02-16 21:09:57 +01:00
RSYNC_TYPE_ARGS=$RSYNC_TYPE_ARGS" --checksum"
fi
if [ "$BANDWIDTH" != "" ] && [ "$BANDWIDTH" != "0" ]; then
RSYNC_ARGS=$RSYNC_ARGS" --bwlimit=$BANDWIDTH"
fi
2016-02-16 21:09:57 +01:00
if [ "$PARTIAL" == "yes" ]; then
RSYNC_ARGS=$RSYNC_ARGS" --partial --partial-dir=\"$PARTIAL_DIR\""
RSYNC_PARTIAL_EXCLUDE="--exclude=\"$PARTIAL_DIR\""
fi
if [ "$DELTA_COPIES" != "no" ]; then
RSYNC_ARGS=$RSYNC_ARGS" --no-whole-file"
else
RSYNC_ARGS=$RSYNC_ARGS" --whole-file"
fi
## Set compression executable and extension
COMPRESSION_LEVEL=3
if type xz > /dev/null 2>&1
then
COMPRESSION_PROGRAM="| xz -$COMPRESSION_LEVEL"
COMPRESSION_EXTENSION=.xz
elif type lzma > /dev/null 2>&1
then
COMPRESSION_PROGRAM="| lzma -$COMPRESSION_LEVEL"
COMPRESSION_EXTENSION=.lzma
elif type pigz > /dev/null 2>&1
then
COMPRESSION_PROGRAM="| pigz -$COMPRESSION_LEVEL"
2016-02-19 22:14:13 +01:00
COMPRESSION_EXTENSION=.gz
# obackup specific
COMPRESSION_OPTIONS=--rsyncable
elif type gzip > /dev/null 2>&1
then
COMPRESSION_PROGRAM="| gzip -$COMPRESSION_LEVEL"
COMPRESSION_EXTENSION=.gz
2016-02-19 22:14:13 +01:00
# obackup specific
COMPRESSION_OPTIONS=--rsyncable
else
COMPRESSION_PROGRAM=
COMPRESSION_EXTENSION=
fi
ALERT_LOG_FILE="$ALERT_LOG_FILE$COMPRESSION_EXTENSION"
}
function PostInit {
# Define remote commands
SSH_CMD="$(type -p ssh) $SSH_COMP -i $SSH_RSA_PRIVATE_KEY $REMOTE_USER@$REMOTE_HOST -p $REMOTE_PORT"
SCP_CMD="$(type -p scp) $SSH_COMP -i $SSH_RSA_PRIVATE_KEY -P $REMOTE_PORT"
RSYNC_SSH_CMD="$(type -p ssh) $SSH_COMP -i $SSH_RSA_PRIVATE_KEY -p $REMOTE_PORT"
}
function InitLocalOSSettings {
## If running under Msys, some commands do not run the same way
## Using mingw version of find instead of windows one
## Getting running processes is quite different
## Ping command is not the same
if [ "$LOCAL_OS" == "msys" ]; then
FIND_CMD=$(dirname $BASH)/find
#TODO: The following command needs to be checked on msys. Does the $1 variable substitution work ?
# PROCESS_TEST_CMD assumes there is a variable $pid
PROCESS_TEST_CMD='ps -a | awk "{\$1=\$1}\$1" | awk "{print \$1}" | grep $pid'
PING_CMD="ping -n 2"
else
FIND_CMD=find
# PROCESS_TEST_CMD assumes there is a variable $pid
PROCESS_TEST_CMD='ps -p$pid'
PING_CMD="ping -c 2 -i .2"
fi
## Stat command has different syntax on Linux and FreeBSD/MacOSX
if [ "$LOCAL_OS" == "MacOSX" ] || [ "$LOCAL_OS" == "BSD" ]; then
STAT_CMD="stat -f \"%Sm\""
else
STAT_CMD="stat --format %y"
fi
}
function InitRemoteOSSettings {
## MacOSX does not use the -E parameter like Linux or BSD does (-E is mapped to extended attrs instead of preserve executability)
if [ "$LOCAL_OS" != "MacOSX" ] && [ "$REMOTE_OS" != "MacOSX" ]; then
2016-02-16 21:09:57 +01:00
RSYNC_ATTR_ARGS=$RSYNC_ATTR_ARGS" -E"
fi
if [ "$REMOTE_OS" == "msys" ]; then
REMOTE_FIND_CMD=$(dirname $BASH)/find
else
REMOTE_FIND_CMD=find
fi
}
## END Generic functions
2015-11-12 01:26:38 +01:00
## Working directory for partial downloads
PARTIAL_DIR=".obackup_workdir_partial"
# List of runtime created global variables
# $SQL_DISK_SPACE, disk space available on target for sql backups
# $FILE_DISK_SPACE, disk space available on target for file backups
# $SQL_BACKUP_TASKS, list of all databases to backup, space separated
# $SQL_EXCLUDED_TASKS, list of all database to exclude from backup, space separated
# $FILE_BACKUP_TASKS list of directories to backup, found in config file
# $FILE_RECURSIVE_BACKUP_TASKS, list of directories to backup, computed from config file recursive list
# $FILE_RECURSIVE_EXCLUDED_TASKS, list of all directories excluded from recursive list
# $FILE_SIZE_LIST, list of all directories to include in GetDirectoriesSize
CAN_BACKUP_SQL=1
CAN_BACKUP_FILES=1
function TrapStop {
2016-03-03 16:33:59 +01:00
Logger "/!\ Manual exit of backup script. Backups may be in inconsistent state." "WARN"
2015-11-12 01:26:38 +01:00
exit 1
}
function TrapQuit {
2016-02-19 22:14:13 +01:00
local exitcode
2015-11-12 01:26:38 +01:00
if [ $ERROR_ALERT -ne 0 ]; then
SendAlert
CleanUp
Logger "Backup script finished with errors." "ERROR"
2016-02-19 22:14:13 +01:00
exitcode=1
2015-11-12 01:26:38 +01:00
elif [ $WARN_ALERT -ne 0 ]; then
SendAlert
CleanUp
Logger "Backup script finished with warnings." "WARN"
2016-02-19 22:14:13 +01:00
exitcode=2
2013-07-16 23:10:27 +02:00
else
2015-11-12 01:26:38 +01:00
CleanUp
Logger "Backup script finshed." "NOTICE"
2016-02-19 22:14:13 +01:00
exitcode=0
2013-07-16 23:10:27 +02:00
fi
2013-09-22 12:04:09 +02:00
2016-03-03 16:06:27 +01:00
if [ -f "$RUN_DIR/$PROGRAM.$INSTANCE_ID" ]; then
rm -f "$RUN_DIR/$PROGRAM.$INSTANCE_ID"
fi
2015-11-12 01:26:38 +01:00
KillChilds $$ > /dev/null 2>&1
2016-02-19 22:14:13 +01:00
exit $exitcode
2015-11-12 01:26:38 +01:00
}
2013-07-16 23:10:27 +02:00
2015-11-12 01:26:38 +01:00
function CheckEnvironment {
if [ "$REMOTE_OPERATION" == "yes" ]; then
if ! type ssh > /dev/null 2>&1 ; then
Logger "ssh not present. Cannot start backup." "CRITICAL"
exit 1
fi
if [ "$SQL_BACKUP" != "no" ]; then
if ! type mysqldump > /dev/null 2>&1 ; then
Logger "mysqldump not present. Cannot backup SQL." "CRITICAL"
CAN_BACKUP_SQL=0
fi
if ! type mysql > /dev/null 2>&1 ; then
Logger "mysql not present. Cannot backup SQL." "CRITICAL"
CAN_BACKUP_SQL=0
fi
fi
fi
if [ "$FILE_BACKUP" != "no" ]; then
if [ "$ENCRYPTION" == "yes" ]; then
if ! type duplicity > /dev/null 2>&1 ; then
Logger "duplicity not present. Cannot backup encrypted files." "CRITICAL"
CAN_BACKUP_FILES=0
2013-07-16 23:10:27 +02:00
fi
else
2015-11-12 01:26:38 +01:00
if ! type rsync > /dev/null 2>&1 ; then
Logger "rsync not present. Cannot backup files." "CRITICAL"
CAN_BACKUP_FILES=0
fi
2013-07-16 23:10:27 +02:00
fi
2015-11-12 01:26:38 +01:00
fi
}
function CheckCurrentConfig {
if [ "$INSTANCE_ID" == "" ]; then
Logger "No INSTANCE_ID defined in config file." "CRITICAL"
exit 1
fi
# Check all variables that should contain "yes" or "no"
declare -a yes_no_vars=(SQL_BACKUP FILE_BACKUP ENCRYPTION CREATE_DIRS KEEP_ABSOLUTE_PATHS GET_BACKUP_SIZE SUDO_EXEC SSH_COMPRESSION REMOTE_HOST_PING DATABASES_ALL PRESERVE_ACL PRESERVE_XATTR COPY_SYMLINKS KEEP_DIRLINKS PRESERVE_HARDLINKS RSYNC_COMPRESS PARTIAL DELETE_VANISHED_FILES DELTA_COPIES ROTATE_SQL_BACKUPS ROTATE_FILE_BACKUPS STOP_ON_CMD_ERROR)
2016-02-19 22:14:13 +01:00
for i in "${yes_no_vars[@]}"; do
2015-11-12 01:26:38 +01:00
test="if [ \"\$$i\" != \"yes\" ] && [ \"\$$i\" != \"no\" ]; then Logger \"Bogus $i value defined in config file.\" \"CRITICAL\"; exit 1; fi"
eval "$test"
2013-09-22 12:04:09 +02:00
done
2013-11-02 18:55:17 +01:00
2015-11-12 01:26:38 +01:00
if [ "$BACKUP_TYPE" != "local" ] && [ "$BACKUP_TYPE" != "pull" ] && [ "$BACKUP_TYPE" != "push" ]; then
Logger "Bogus BACKUP_TYPE value in config file." "CRITICAL"
exit 1
2013-11-02 18:55:17 +01:00
fi
2015-11-12 01:26:38 +01:00
# Check all variables that should contain a numerical value >= 0
declare -a num_vars=(BACKUP_SIZE_MINIMUM BANDWIDTH SQL_WARN_MIN_SPACE FILE_WARN_MIN_SPACE SOFT_MAX_EXEC_TIME_DB_TASK HARD_MAX_EXEC_TIME_DB_TASK COMPRESSION_LEVEL SOFT_MAX_EXEC_TIME_FILE_TASK HARD_MAX_EXEC_TIME_FILE_TASK SOFT_MAX_EXEC_TIME_TOTAL HARD_MAX_EXEC_TIME_TOTAL ROTATE_SQL_COPIES ROTATE_FILE_COPIES MAX_EXEC_TIME_PER_CMD_BEFORE MAX_EXEC_TIME_PER_CMD_AFTER)
2016-02-19 22:14:13 +01:00
for i in "${num_vars[@]}"; do
2015-11-12 01:26:38 +01:00
test="if [ $(IsNumeric \"\$$i\") -eq 0 ]; then Logger \"Bogus $i value defined in config file.\" \"CRITICAL\"; exit 1; fi"
eval "$test"
done
if [ "$FILE_BACKUP" == "yes" ]; then
if [ "$DIRECTORY_LIST" == "" ] && [ "$RECURSIVE_DIRECTORY_LIST" == "" ]; then
Logger "No directories specified in config file, no files to backup." "ERROR"
CAN_BACKUP_FILES=0
fi
fi
2015-11-12 01:26:38 +01:00
#TODO-v2.1: Add runtime variable tests (RSYNC_ARGS etc)
2013-07-16 23:10:27 +02:00
}
2016-03-03 16:06:27 +01:00
function CheckRunningInstances {
if [ -f "$RUN_DIR/$PROGRAM.$INSTANCE_ID" ]; then
pid=$(cat "$RUN_DIR/$PROGRAM.$INSTANCE_ID")
if ps aux | awk '{print $2}' | grep $pid > /dev/null; then
Logger "Another instance [$INSTANCE_ID] of obackup is already running." "CRITICAL"
exit 1
fi
fi
echo $SCRIPT_PID > "$RUN_DIR/$PROGRAM.$INSTANCE_ID"
}
2015-11-12 01:26:38 +01:00
function _ListDatabasesLocal {
2016-02-19 22:14:13 +01:00
local sql_cmd=
sql_cmd="mysql -u $SQL_USER -Bse 'SELECT table_schema, round(sum( data_length + index_length ) / 1024) FROM information_schema.TABLES GROUP by table_schema;' > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1"
2015-11-12 01:26:38 +01:00
Logger "cmd: $sql_cmd" "DEBUG"
eval "$sql_cmd" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_DB_TASK $HARD_MAX_EXEC_TIME_DB_TASK ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? -eq 0 ]; then
Logger "Listing databases succeeded." "NOTICE"
else
Logger "Listing databases failed." "ERROR"
2016-02-19 22:14:13 +01:00
if [ -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then
Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
fi
return 1
fi
}
function _ListDatabasesRemote {
2016-02-19 22:14:13 +01:00
local sql_cmd=
2013-07-16 23:10:27 +02:00
CheckConnectivity3rdPartyHosts
2015-11-12 01:26:38 +01:00
CheckConnectivityRemoteHost
2016-02-19 22:14:13 +01:00
sql_cmd="$SSH_CMD \"mysql -u $SQL_USER -Bse 'SELECT table_schema, round(sum( data_length + index_length ) / 1024) FROM information_schema.TABLES GROUP by table_schema;'\" > \"$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID\" 2>&1"
2015-11-12 01:26:38 +01:00
Logger "cmd: $sql_cmd" "DEBUG"
eval "$sql_cmd" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_DB_TASK $HARD_MAX_EXEC_TIME_DB_TASK ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? -eq 0 ]; then
Logger "Listing databases succeeded." "NOTICE"
else
Logger "Listing databases failed." "ERROR"
2016-02-19 22:14:13 +01:00
if [ -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then
Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
2013-07-16 23:10:27 +02:00
fi
2015-11-12 01:26:38 +01:00
return 1
fi
}
function ListDatabases {
local output_file # Return of subfunction
2016-02-19 22:14:13 +01:00
local db_name
local db_size
local db_backup
2015-11-12 01:26:38 +01:00
if [ $CAN_BACKUP_SQL -ne 1 ]; then
Logger "Cannot list databases." "ERROR"
return 1
fi
Logger "Listing databases." "NOTICE"
if [ "$BACKUP_TYPE" == "local" ] || [ "$BACKUP_TYPE" == "push" ]; then
_ListDatabasesLocal
if [ $? != 0 ]; then
output_file=""
else
output_file="$RUN_DIR/$PROGRAM._ListDatabasesLocal.$SCRIPT_PID"
fi
elif [ "$BACKUP_TYPE" == "pull" ]; then
_ListDatabasesRemote
if [ $? != 0 ]; then
output_file=""
else
output_file="$RUN_DIR/$PROGRAM._ListDatabasesRemote.$SCRIPT_PID"
2013-07-16 23:10:27 +02:00
fi
fi
2013-11-02 18:55:17 +01:00
2015-11-12 01:26:38 +01:00
if [ -f "$output_file" ] && [ $CAN_BACKUP_SQL -eq 1 ]; then
OLD_IFS=$IFS
IFS=$' \n'
for line in $(cat "$output_file")
do
db_name=$(echo $line | cut -f1)
db_size=$(echo $line | cut -f2)
if [ "$DATABASES_ALL" == "yes" ]; then
db_backup=1
IFS=$PATH_SEPARATOR_CHAR
for j in $DATABASES_ALL_EXCLUDE_LIST
do
if [ "$db_name" == "$j" ]; then
db_backup=0
fi
done
IFS=$' \n'
else
db_backup=0
IFS=$PATH_SEPARATOR_CHAR
for j in $DATABASES_LIST
do
if [ "$db_name" == "$j" ]; then
db_backup=1
fi
done
IFS=$' \n'
fi
if [ $db_backup -eq 1 ]; then
if [ "$SQL_BACKUP_TASKS" != "" ]; then
SQL_BACKUP_TASKS="$SQL_BACKUP_TASKS $db_name"
else
SQL_BACKUP_TASKS="$db_name"
fi
TOTAL_DATABASES_SIZE=$((TOTAL_DATABASES_SIZE+$db_size))
else
SQL_EXCLUDED_TASKS="$SQL_EXCLUDED_TASKS $db_name"
fi
done
IFS=$OLD_IFS
Logger "Database backup list: $SQL_BACKUP_TASKS" "DEBUG"
Logger "Database exclude list: $SQL_EXCLUDED_TASKS" "DEBUG"
2014-11-27 14:40:30 +01:00
else
2015-11-12 01:26:38 +01:00
Logger "Will not execute database backup." "ERROR"
CAN_BACKUP_SQL=0
2014-11-27 14:40:30 +01:00
fi
2013-07-16 23:10:27 +02:00
}
2015-11-12 01:26:38 +01:00
function _ListRecursiveBackupDirectoriesLocal {
2016-02-19 22:14:13 +01:00
local cmd
OLD_IFS=$IFS
2015-11-12 01:26:38 +01:00
IFS=$PATH_SEPARATOR_CHAR
for directory in $RECURSIVE_DIRECTORY_LIST
2013-07-16 23:10:27 +02:00
do
# No sudo here, assuming you should have all necessary rights for local checks
2016-02-19 22:14:13 +01:00
cmd="$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"
2015-11-12 01:26:38 +01:00
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_FILE_TASK $HARD_MAX_EXEC_TIME_FILE_TASK ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
Logger "Could not enumerate directories in [$directory]." "ERROR"
2016-02-19 22:14:13 +01:00
if [ -f $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID ]; then
Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
fi
2016-02-19 22:14:13 +01:00
if [ -f $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID ]; then
Logger "Error output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
fi
retval=1
2013-07-16 23:10:27 +02:00
else
2015-11-12 01:26:38 +01:00
retval=0
2013-07-16 23:10:27 +02:00
fi
done
IFS=$OLD_IFS
2015-11-12 01:26:38 +01:00
return $retval
2013-07-16 23:10:27 +02:00
}
2015-11-12 01:26:38 +01:00
function _ListRecursiveBackupDirectoriesRemote {
2016-02-19 22:14:13 +01:00
local cmd
2013-07-16 23:10:27 +02:00
OLD_IFS=$IFS
IFS=$PATH_SEPARATOR_CHAR
2015-11-12 01:26:38 +01:00
for directory in $RECURSIVE_DIRECTORY_LIST
2013-07-16 23:10:27 +02:00
do
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
2015-11-12 01:26:38 +01:00
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_FILE_TASK $HARD_MAX_EXEC_TIME_FILE_TASK ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
Logger "Could not enumerate directories in [$directory]." "ERROR"
2016-02-19 22:14:13 +01:00
if [ -f $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID ]; then
Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
fi
2016-02-19 22:14:13 +01:00
if [ -f $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID ]; then
Logger "Error output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID)" "ERROR"
2013-07-16 23:10:27 +02:00
fi
2015-11-12 01:26:38 +01:00
retval=1
2013-07-16 23:10:27 +02:00
else
2015-11-12 01:26:38 +01:00
retval=0
2013-07-16 23:10:27 +02:00
fi
2015-11-12 01:26:38 +01:00
done
IFS=$OLD_IFS
return $retval
}
function ListRecursiveBackupDirectories {
2016-02-19 22:14:13 +01:00
local output_file
local file_exclude
2015-11-12 01:26:38 +01:00
Logger "Listing directories to backup." "NOTICE"
if [ "$BACKUP_TYPE" == "local" ] || [ "$BACKUP_TYPE" == "push" ]; then
_ListRecursiveBackupDirectoriesLocal
if [ $? != 0 ]; then
output_file=""
else
output_file="$RUN_DIR/$PROGRAM._ListRecursiveBackupDirectoriesLocal.$SCRIPT_PID"
fi
elif [ "$BACKUP_TYPE" == "pull" ]; then
_ListRecursiveBackupDirectoriesRemote
if [ $? != 0 ]; then
output_file=""
else
output_file="$RUN_DIR/$PROGRAM._ListRecursiveBackupDirectoriesRemote.$SCRIPT_PID"
2013-07-16 23:10:27 +02:00
fi
2015-11-12 01:26:38 +01:00
fi
2013-07-16 23:10:27 +02:00
2015-11-12 01:26:38 +01:00
if [ -f "$output_file" ]; then
2013-09-22 12:04:09 +02:00
OLD_IFS=$IFS
IFS=$' \n'
2015-11-12 01:26:38 +01:00
for line in $(cat "$output_file")
2013-07-16 23:10:27 +02:00
do
file_exclude=0
IFS=$PATH_SEPARATOR_CHAR
2015-11-12 01:26:38 +01:00
for k in $RECURSIVE_EXCLUDE_LIST
2013-07-16 23:10:27 +02:00
do
2015-11-12 01:26:38 +01:00
if [ "$k" == "$line" ]; then
2013-07-16 23:10:27 +02:00
file_exclude=1
fi
done
IFS=$' \n'
2014-09-22 22:45:18 +02:00
2015-11-12 01:26:38 +01:00
if [ $file_exclude -eq 0 ]; then
if [ "$FILE_RECURSIVE_BACKUP_TASKS" == "" ]; then
FILE_RECURSIVE_BACKUP_TASKS="$line"
FILE_SIZE_LIST="$(EscapeSpaces $line)"
2013-07-16 23:10:27 +02:00
else
2015-11-12 01:26:38 +01:00
FILE_RECURSIVE_BACKUP_TASKS="$FILE_RECURSIVE_BACKUP_TASKS$PATH_SEPARATOR_CHAR$line"
FILE_SIZE_LIST="$FILE_SIZE_LIST $(EscapeSpaces $line)"
2013-07-16 23:10:27 +02:00
fi
else
2015-11-12 01:26:38 +01:00
FILE_RECURSIVE_EXCLUDED_TASKS="$FILE_RECURSIVE_EXCLUDED_TASKS$PATH_SEPARATOR_CHAR$line"
2013-07-16 23:10:27 +02:00
fi
2013-09-22 12:04:09 +02:00
done
2015-11-12 01:26:38 +01:00
IFS=$OLD_IFS
fi
OLD_IFS=$IFS
IFS=$PATH_SEPARATOR_CHAR
for directory in $DIRECTORY_LIST
do
2015-11-15 14:47:20 +01:00
FILE_SIZE_LIST="$FILE_SIZE_LIST $(EscapeSpaces $directory)"
2015-11-12 01:26:38 +01:00
if [ "$FILE_BACKUP_TASKS" == "" ]; then
FILE_BACKUP_TASKS="$directory"
else
FILE_BACKUP_TASKS="$FILE_BACKUP_TASKS$PATH_SEPARATOR_CHAR$directory"
fi
done
IFS=$OLD_IFS
}
function _GetDirectoriesSizeLocal {
local dir_list="${1}"
2016-02-19 22:14:13 +01:00
local cmd
# No sudo here, assuming you should have all the necessary rights
2016-02-19 22:14:13 +01:00
cmd='echo "'$dir_list'" | xargs du -cs | tail -n1 | cut -f1 > '$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2> $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID
2015-11-12 01:26:38 +01:00
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_FILE_TASK $HARD_MAX_EXEC_TIME_FILE_TASK ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
# $cmd will return 0 even if some errors found, so we need to check if there is an error output
2016-02-19 22:14:13 +01:00
if [ $? != 0 ] || [ -s $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID ]; then
2015-11-12 01:26:38 +01:00
Logger "Could not get files size for some or all directories." "ERROR"
2016-02-19 22:14:13 +01:00
if [ -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then
Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
fi
2016-02-19 22:14:13 +01:00
if [ -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID" ]; then
Logger "Error output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
fi
else
Logger "File size fetched successfully." "NOTICE"
fi
2016-02-19 22:14:13 +01:00
if [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then
TOTAL_FILES_SIZE="$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)"
2015-11-12 01:26:38 +01:00
else
TOTAL_FILES_SIZE=-1
fi
}
function _GetDirectoriesSizeRemote {
local dir_list="${1}"
2016-02-19 22:14:13 +01:00
local cmd
2015-11-12 01:26:38 +01:00
# Error output is different from stdout because not all files in list may fail at once
2016-02-19 22:14:13 +01:00
cmd=$SSH_CMD' "echo '$dir_list' | xargs '$COMMAND_SUDO' du -cs | tail -n1 | cut -f1" > '$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID' 2> '$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID
2015-11-12 01:26:38 +01:00
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_FILE_TASK $HARD_MAX_EXEC_TIME_FILE_TASK ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
# $cmd will return 0 even if some errors found, so we need to check if there is an error output
2016-02-19 22:14:13 +01:00
if [ $? != 0 ] || [ -s $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID ]; then
2015-11-12 01:26:38 +01:00
Logger "Could not get files size for some or all directories." "ERROR"
2016-02-19 22:14:13 +01:00
if [ -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then
Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
fi
2016-02-19 22:14:13 +01:00
if [ -f "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID" ]; then
Logger "Error output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
fi
else
Logger "File size fetched successfully." "NOTICE"
fi
2016-02-19 22:14:13 +01:00
if [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then
TOTAL_FILES_SIZE="$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)"
2015-11-12 01:26:38 +01:00
else
TOTAL_FILES_SIZE=-1
fi
}
function GetDirectoriesSize {
Logger "Getting files size" "NOTICE"
if [ "$BACKUP_TYPE" == "local" ] || [ "$BACKUP_TYPE" == "push" ]; then
if [ "$FILE_BACKUP" != "no" ]; then
_GetDirectoriesSizeLocal "$FILE_SIZE_LIST"
fi
elif [ "$BACKUP_TYPE" == "pull" ]; then
if [ "$FILE_BACKUP" != "no" ]; then
_GetDirectoriesSizeRemote "$FILE_SIZE_LIST"
fi
fi
}
function _CreateDirectoryLocal {
2015-11-12 01:26:38 +01:00
local dir_to_create="${1}"
if [ ! -d "$dir_to_create" ]; then
# No sudo, you should have all necessary rights
2016-02-19 22:14:13 +01:00
mkdir --parents "$dir_to_create" > $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID 2>&1
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
Logger "Cannot create directory [$dir_to_create]" "CRITICAL"
2016-02-19 22:14:13 +01:00
if [ -f $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID ]; then
Logger "Command output: $(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
fi
return 1
fi
fi
}
function _CreateDirectoryRemote {
2015-11-12 01:26:38 +01:00
local dir_to_create="${1}"
2016-02-19 22:14:13 +01:00
local cmd
2015-11-12 01:26:38 +01:00
CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost
2016-02-19 22:14:13 +01:00
cmd=$SSH_CMD' "if ! [ -d \"'$dir_to_create'\" ]; then '$COMMAND_SUDO' mkdir --parents \"'$dir_to_create'\"; fi" > '$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID' 2>&1'
2015-11-12 01:26:38 +01:00
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! 720 1800 ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
Logger "Cannot create remote directory [$dir_to_create]." "CRITICAL"
2016-02-19 22:14:13 +01:00
Logger "Command output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
return 1
fi
}
function CreateStorageDirectories {
if [ "$BACKUP_TYPE" == "local" ] || [ "$BACKUP_TYPE" == "pull" ]; then
if [ "$SQL_BACKUP" != "no" ]; then
_CreateDirectoryLocal "$SQL_STORAGE"
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
CAN_BACKUP_SQL=0
fi
fi
if [ "$FILE_BACKUP" != "no" ]; then
_CreateDirectoryLocal "$FILE_STORAGE"
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
CAN_BACKUP_FILES=0
fi
fi
elif [ "$BACKUP_TYPE" == "push" ]; then
if [ "$SQL_BACKUP" != "no" ]; then
_CreateDirectoryRemote "$SQL_STORAGE"
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
CAN_BACKUP_SQL=0
fi
fi
if [ "$FILE_BACKUP" != "no" ]; then
_CreateDirectoryRemote "$FILE_STORAGE"
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
CAN_BACKUP_FILES=0
fi
fi
fi
}
function GetDiskSpaceLocal {
# GLOBAL VARIABLE DISK_SPACE to pass variable to parent function
# GLOBAL VARIABLE DRIVE to pass variable to parent function
local path_to_check="${1}"
if [ -d "$path_to_check" ]; then
2015-11-12 01:26:38 +01:00
# Not elegant solution to make df silent on errors
# No sudo on local commands, assuming you should have all the necesarry rights to check backup directories sizes
2016-02-19 22:14:13 +01:00
df -P "$path_to_check" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" 2>&1
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
DISK_SPACE=0
Logger "Cannot get disk space in [$path_to_check] on local system." "ERROR"
2016-02-19 22:14:13 +01:00
Logger "Command Output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
else
2016-02-19 22:14:13 +01:00
DISK_SPACE=$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID | tail -1 | awk '{print $4}')
DRIVE=$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID | tail -1 | awk '{print $1}')
2015-11-12 01:26:38 +01:00
fi
else
Logger "Storage path [$path_to_check] does not exist." "CRITICAL"
2015-11-12 01:26:38 +01:00
return 1
fi
}
function GetDiskSpaceRemote {
# USE GLOBAL VARIABLE DISK_SPACE to pass variable to parent function
local path_to_check="${1}"
2016-02-19 22:14:13 +01:00
local cmd
cmd=$SSH_CMD' "if [ -d \"'$path_to_check'\" ]; then '$COMMAND_SUDO' df -P \"'$path_to_check'\"; else exit 1; fi" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1'
2015-11-12 01:26:38 +01:00
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_DB_TASK $HARD_MAX_EXEC_TIME_DB_TASK ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
DISK_SPACE=0
Logger "Cannot get disk space in [$path_to_check] on remote system." "ERROR"
2016-02-19 22:14:13 +01:00
Logger "Command Output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
return 1
else
2016-02-19 22:14:13 +01:00
DISK_SPACE=$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID | tail -1 | awk '{print $4}')
DRIVE=$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID | tail -1 | awk '{print $1}')
2015-11-12 01:26:38 +01:00
fi
}
function CheckDiskSpace {
# USE OF GLOBAL VARIABLES TOTAL_DATABASES_SIZE, TOTAL_FILES_SIZE, BACKUP_SIZE_MINIMUM, STORAGE_WARN_SIZE, STORAGE_SPACE
if [ "$BACKUP_TYPE" == "local" ] || [ "$BACKUP_TYPE" == "pull" ]; then
if [ "$SQL_BACKUP" != "no" ]; then
GetDiskSpaceLocal "$SQL_STORAGE"
if [ $? != 0 ]; then
SQL_DISK_SPACE=0
CAN_BACKUP_SQL=0
else
SQL_DISK_SPACE=$DISK_SPACE
SQL_DRIVE=$DRIVE
fi
fi
if [ "$FILE_BACKUP" != "no" ]; then
GetDiskSpaceLocal "$FILE_STORAGE"
if [ $? != 0 ]; then
FILE_DISK_SPACE=0
CAN_BACKUP_FILES=0
else
FILE_DISK_SPACE=$DISK_SPACE
FILE_DRIVE=$DRIVE
fi
fi
elif [ "$BACKUP_TYPE" == "push" ]; then
if [ "$SQL_BACKUP" != "no" ]; then
GetDiskSpaceRemote "$SQL_STORAGE"
if [ $? != 0 ]; then
SQL_DISK_SPACE=0
else
SQL_DISK_SPACE=$DISK_SPACE
SQL_DRIVE=$DRIVE
fi
fi
if [ "$FILE_BACKUP" != "no" ]; then
GetDiskSpaceRemote "$FILE_STORAGE"
if [ $? != 0 ]; then
FILE_DISK_SPACE=0
else
FILE_DISK_SPACE=$DISK_SPACE
FILE_DRIVE=$DRIVE
fi
fi
fi
if [ "$TOTAL_DATABASES_SIZE" == "" ]; then
TOTAL_DATABASES_SIZE=-1
fi
if [ "$TOTAL_FILES_SIZE" == "" ]; then
TOTAL_FILES_SIZE=-1
fi
if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL -eq 1 ]; then
if [ $SQL_DISK_SPACE -eq 0 ]; then
Logger "Storage space in [$SQL_STORAGE] reported to be 0Ko." "WARN"
fi
if [ $SQL_DISK_SPACE -lt $TOTAL_DATABASES_SIZE ]; then
Logger "Disk space in [$SQL_STORAGE] may be insufficient to backup SQL ($SQL_DISK_SPACE Ko available in $SQL_DRIVE) (non compressed databases calculation)." "WARN"
fi
if [ $SQL_DISK_SPACE -lt $SQL_WARN_MIN_SPACE ]; then
Logger "Disk space in [$SQL_STORAGE] is lower than warning value [$SQL_WARN_MIN_SPACE Ko]." "WARN"
fi
Logger "SQL storage Space: $SQL_DISK_SPACE Ko - Databases size: $TOTAL_DATABASES_SIZE Ko" "NOTICE"
fi
if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES -eq 1 ]; then
if [ $FILE_DISK_SPACE -eq 0 ]; then
Logger "Storage space in [$FILE_STORAGE] reported to be 0 Ko." "WARN"
fi
if [ $FILE_DISK_SPACE -lt $TOTAL_FILES_SIZE ]; then
Logger "Disk space in [$FILE_STORAGE] may be insufficient to backup files ($FILE_DISK_SPACE Ko available in $FILE_DRIVE)." "WARN"
fi
if [ $FILE_DISK_SPACE -lt $FILE_WARN_MIN_SPACE ]; then
Logger "Disk space in [$FILE_STORAGE] is lower than warning value [$FILE_WARN_MIN_SPACE Ko]." "WARN"
fi
Logger "File storage space: $FILE_DISK_SPACE Ko - Files size: $TOTAL_FILES_SIZE Ko" "NOTICE"
fi
if [ $BACKUP_SIZE_MINIMUM -gt $(($TOTAL_DATABASES_SIZE+$TOTAL_FILES_SIZE)) ] && [ "$GET_BACKUP_SIZE" != "no" ]; then
Logger "Backup size is smaller than expected." "WARN"
fi
}
function _BackupDatabaseLocalToLocal {
local database="${1}" # Database to backup
local export_options="${2}" # export options
2016-02-19 22:14:13 +01:00
local dry_sql_cmd
local sql_cmd
2015-11-12 01:26:38 +01:00
2016-02-19 22:14:13 +01:00
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"
2015-11-12 01:26:38 +01:00
if [ $_DRYRUN -ne 1 ]; then
Logger "cmd: $sql_cmd" "DEBUG"
eval "$sql_cmd" &
else
Logger "cmd: $dry_sql_cmd" "DEBUG"
eval "$dry_sql_cmd" &
fi
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_DB_TASK $HARD_MAX_EXEC_TIME_DB_TASK ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
local retval=$?
2016-02-19 22:14:13 +01:00
if [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID" ]; then
Logger "Error output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
fi
return $retval
}
function _BackupDatabaseLocalToRemote {
local database="${1}" # Database to backup
local export_options="${2}" # export options
2016-02-19 22:14:13 +01:00
local dry_sql_cmd
local sql_cmd
2015-11-12 01:26:38 +01:00
CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost
#TODO-v2.0: cannot catch mysqldump warnings
2016-02-19 22:14:13 +01:00
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"
2015-11-12 01:26:38 +01:00
if [ $_DRYRUN -ne 1 ]; then
Logger "cmd: $sql_cmd" "DEBUG"
eval "$sql_cmd" &
else
Logger "cmd: $dry_sql_cmd" "DEBUG"
eval "$dry_sql_cmd" &
fi
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_DB_TASK $HARD_MAX_EXEC_TIME_DB_TASK ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
local retval=$?
2016-02-19 22:14:13 +01:00
if [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID" ]; then
Logger "Error output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
fi
return $retval
}
function _BackupDatabaseRemoteToLocal {
local database="${1}" # Database to backup
local export_options="${2}" # export options
2016-02-19 22:14:13 +01:00
local dry_sql_cmd
local sql_cmd
local retval
2015-11-12 01:26:38 +01:00
CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost
2016-02-19 22:14:13 +01:00
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'"'
2015-11-12 01:26:38 +01:00
if [ $_DRYRUN -ne 1 ]; then
Logger "cmd: $sql_cmd" "DEBUG"
eval "$sql_cmd" &
else
Logger "cmd: $dry_sql_cmd" "DEBUG"
eval "$dry_sql_cmd" &
fi
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_DB_TASK $HARD_MAX_EXEC_TIME_DB_TASK ${FUNCNAME[0]}
retval=$?
if [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID" ]; then
Logger "Error output:\n$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.error.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
fi
return $retval
}
function BackupDatabase {
local database="${1}"
2016-02-19 22:14:13 +01:00
local mysql_options
2015-11-12 01:26:38 +01:00
# Hack to prevent warning on table mysql.events, some mysql versions don't support --skip-events, prefer using --ignore-table
if [ "$database" == "mysql" ]; then
2016-02-19 22:14:13 +01:00
mysql_options='--skip-lock-tables --single-transaction --ignore-table=mysql.event'
2015-11-12 01:26:38 +01:00
else
2016-02-19 22:14:13 +01:00
mysql_options='--skip-lock-tables --single-transaction'
2015-11-12 01:26:38 +01:00
fi
if [ "$BACKUP_TYPE" == "local" ]; then
_BackupDatabaseLocalToLocal "$database" "$mysql_options"
elif [ "$BACKUP_TYPE" == "pull" ]; then
_BackupDatabaseRemoteToLocal "$database" "$mysql_options"
elif [ "$BACKUP_TYPE" == "push" ]; then
_BackupDatabaseLocalToRemote "$database" "$mysql_options"
fi
if [ $? -ne 0 ]; then
Logger "Backup failed." "ERROR"
else
Logger "Backup succeeded." "NOTICE"
fi
}
function BackupDatabases {
local database
OLD_IFS=$IFS
IFS=$' \t\n'
for database in $SQL_BACKUP_TASKS
do
Logger "Backing up database [$database]." "NOTICE"
BackupDatabase $database &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_DB_TASK $HARD_MAX_EXEC_TIME_DB_TASK ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
CheckTotalExecutionTime
done
IFS=$OLD_IFS
}
function Rsync {
local backup_directory="${1}" # Which directory to backup
local is_recursive="${2}" # Backup only files at toplevel of directory
2016-02-19 22:14:13 +01:00
local file_storage_path
local rsync_cmd
2015-11-12 01:26:38 +01:00
if [ "$KEEP_ABSOLUTE_PATHS" == "yes" ]; then
2016-02-19 22:14:13 +01:00
file_storage_path="$(dirname $FILE_STORAGE/${backup_directory#/})"
2015-11-12 01:26:38 +01:00
else
2016-02-19 22:14:13 +01:00
file_storage_path="$FILE_STORAGE"
2015-11-12 01:26:38 +01:00
fi
## Manage to backup recursive directories lists files only (not recursing into subdirectories)
if [ "$is_recursive" == "no-recurse" ]; then
# Fixes symlinks to directories in target cannot be deleted when backing up root directory without recursion, and excludes subdirectories
RSYNC_NO_RECURSE_ARGS=" -k --exclude=*/*/"
else
RSYNC_NO_RECURSE_ARGS=""
fi
# Creating subdirectories because rsync cannot handle multiple subdirectory creation
2015-11-12 01:26:38 +01:00
if [ "$BACKUP_TYPE" == "local" ]; then
_CreateDirectoryLocal "$file_storage_path"
2016-02-19 22:14:13 +01:00
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"
2015-11-12 01:26:38 +01:00
elif [ "$BACKUP_TYPE" == "pull" ]; then
_CreateDirectoryLocal "$file_storage_path"
2015-11-12 01:26:38 +01:00
CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost
2016-02-19 22:14:13 +01:00
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"
2015-11-12 01:26:38 +01:00
elif [ "$BACKUP_TYPE" == "push" ]; then
CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost
_CreateDirectoryRemote "$file_storage_path"
2016-02-19 22:14:13 +01:00
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"
2015-11-12 01:26:38 +01:00
fi
Logger "cmd: $rsync_cmd" "DEBUG"
eval "$rsync_cmd" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_FILE_TASK $HARD_MAX_EXEC_TIME_FILE_TASK ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
Logger "Failed to backup [$backup_directory] to [$file_storage_path]." "ERROR"
2016-02-19 22:14:13 +01:00
Logger "Command output:\n $(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
else
Logger "File backup succeed." "NOTICE"
fi
}
function Duplicity {
local backup_directory="${1}" # Which directory to backup
local is_recursive="${2}" # Backup only files at toplevel of directory
2016-02-19 22:14:13 +01:00
local file_storage_path
local duplicity_cmd
2015-11-12 01:26:38 +01:00
Logger "Encrpytion not supported yet ! No backup done." "CRITICAL"
return 1
if [ "$KEEP_ABSOLUTE_PATHS" == "yes" ]; then
2016-02-19 22:14:13 +01:00
file_storage_path="$(dirname $FILE_STORAGE$backup_directory)"
2015-11-12 01:26:38 +01:00
else
2016-02-19 22:14:13 +01:00
file_storage_path="$FILE_STORAGE"
2015-11-12 01:26:38 +01:00
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" &
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_FILE_TASK $HARD_MAX_EXEC_TIME_FILE_TASK ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
Logger "Failed to backup [$backup_directory] to [$file_storage_path]." "ERROR"
2016-02-19 22:14:13 +01:00
Logger "Command output:\n $(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
else
Logger "File backup succeed." "NOTICE"
fi
}
function FilesBackup {
OLD_IFS=$IFS
IFS=$PATH_SEPARATOR_CHAR
# Backup non recursive directories
for BACKUP_TASK in $FILE_BACKUP_TASKS
do
Logger "Beginning file backup of [$BACKUP_TASK]." "NOTICE"
if [ "$ENCRYPTION" == "yes" ]; then
Duplicity "$BACKUP_TASK" "recurse"
else
Rsync "$BACKUP_TASK" "recurse"
2013-11-02 18:55:17 +01:00
fi
2015-11-12 01:26:38 +01:00
CheckTotalExecutionTime
2013-07-16 23:10:27 +02:00
done
2014-09-22 22:45:18 +02:00
2015-11-12 01:26:38 +01:00
## Backup files at root of DIRECTORIES_RECURSE_LIST directories
for BACKUP_TASK in $RECURSIVE_DIRECTORY_LIST
2013-07-16 23:10:27 +02:00
do
2015-11-12 01:26:38 +01:00
Logger "Beginning non recursive file backup of [$BACKUP_TASK]." "NOTICE"
if [ "$ENCRYPTION" == "yes" ]; then
Duplicity "$BACKUP_TASK" "no-recurse"
2013-07-16 23:10:27 +02:00
else
2015-11-12 01:26:38 +01:00
Rsync "$BACKUP_TASK" "no-recurse"
2013-07-16 23:10:27 +02:00
fi
2015-11-12 01:26:38 +01:00
CheckTotalExecutionTime
2013-07-16 23:10:27 +02:00
done
2015-11-12 01:26:38 +01:00
# Backup sub directories of recursive directories
for BACKUP_TASK in $FILE_RECURSIVE_BACKUP_TASKS
do
Logger "Beginning recursive file backup of [$BACKUP_TASK]." "NOTICE"
if [ "$ENCRYPTION" == "yes" ]; then
Duplicity "$BACKUP_TASK" "recurse"
else
Rsync "$BACKUP_TASK" "recurse"
fi
CheckTotalExecutionTime
done
2013-07-16 23:10:27 +02:00
IFS=$OLD_IFS
}
2015-11-12 01:26:38 +01:00
function CheckTotalExecutionTime {
#### Check if max execution time of whole script as been reached
if [ $SECONDS -gt $SOFT_MAX_EXEC_TIME_TOTAL ]; then
Logger "Max soft execution time of the whole backup exceeded while backing up [$BACKUP_TASK]." "ERROR"
WARN_ALERT=1
SendAlert
if [ $SECONDS -gt $HARD_MAX_EXEC_TIME_TOTAL ] && [ $HARD_MAX_EXEC_TIME_TOTAL -ne 0 ]; then
Logger "Max hard execution time of the whole backup exceeded while backing up [$BACKUP_TASK], stopping backup process." "CRITICAL"
exit 1
2013-07-16 23:10:27 +02:00
fi
fi
}
2015-11-12 01:26:38 +01:00
function _RotateBackupsLocal {
local backup_path="${1}"
local rotate_copies="${2}"
2015-11-12 01:26:38 +01:00
2016-02-19 22:14:13 +01:00
local backup
local copy
local cmd
local path
2013-07-16 23:10:27 +02:00
OLD_IFS=$IFS
2015-11-12 01:26:38 +01:00
IFS=$'\t\n'
for backup in $(ls -I "*.$PROGRAM.*" "$backup_path")
2013-07-16 23:10:27 +02:00
do
copy=$rotate_copies
2015-11-12 01:26:38 +01:00
while [ $copy -gt 1 ]
do
if [ $copy -eq $rotate_copies ]; then
cmd="rm -rf \"$backup_path/$backup.$PROGRAM.$copy\""
2015-11-12 01:26:38 +01:00
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-03-01 19:35:40 +01:00
WaitForTaskCompletion $! 3600 0 ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
Logger "Cannot delete oldest copy [$backup_path/$backup.$PROGRAM.$copy]." "ERROR"
fi
fi
path="$backup_path/$backup.$PROGRAM.$(($copy-1))"
if [[ -f $path || -d $path ]]; then
cmd="mv \"$path\" \"$backup_path/$backup.$PROGRAM.$copy\""
2015-11-12 01:26:38 +01:00
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-03-01 19:35:40 +01:00
WaitForTaskCompletion $! 3600 0 ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
Logger "Cannot move [$path] to [$backup_path/$backup.$PROGRAM.$copy]." "ERROR"
fi
2013-07-16 23:10:27 +02:00
2015-11-12 01:26:38 +01:00
fi
copy=$(($copy-1))
done
2013-11-02 18:55:17 +01:00
2015-11-12 01:26:38 +01:00
# Latest file backup will not be moved if script configured for remote backup so next rsync execution will only do delta copy instead of full one
if [[ $backup == *.sql.* ]]; then
cmd="mv \"$backup_path/$backup\" \"$backup_path/$backup.$PROGRAM.1\""
2015-11-12 01:26:38 +01:00
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-03-01 19:35:40 +01:00
WaitForTaskCompletion $! 3600 0 ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
Logger "Cannot move [$backup_path/$backup] to [$backup_path/$backup.$PROGRAM.1]." "ERROR"
fi
2013-08-25 12:39:13 +02:00
2015-11-12 01:26:38 +01:00
elif [ "$REMOTE_OPERATION" == "yes" ]; then
cmd="cp -R \"$backup_path/$backup\" \"$backup_path/$backup.$PROGRAM.1\""
2015-11-12 01:26:38 +01:00
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-03-01 19:35:40 +01:00
WaitForTaskCompletion $! 3600 0 ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
Logger "Cannot copy [$backup_path/$backup] to [$backup_path/$backup.$PROGRAM.1]." "ERROR"
2013-07-16 23:10:27 +02:00
fi
2014-09-22 22:45:18 +02:00
2015-11-12 01:26:38 +01:00
else
cmd="mv \"$backup_path/$backup\" \"$backup_path/$backup.$PROGRAM.1\""
2015-11-12 01:26:38 +01:00
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
2016-03-01 19:35:40 +01:00
WaitForTaskCompletion $! 3600 0 ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
Logger "Cannot move [$backup_path/$backup] to [$backup_path/$backup.$PROGRAM.1]." "ERROR"
fi
fi
done
2013-07-16 23:10:27 +02:00
IFS=$OLD_IFS
}
2015-11-12 01:26:38 +01:00
function _RotateBackupsRemote {
local backup_path="${1}"
local rotate_copies="${2}"
2016-02-19 22:14:13 +01:00
$SSH_CMD PROGRAM=$PROGRAM REMOTE_OPERATION=$REMOTE_OPERATION _DEBUG=$_DEBUG COMMAND_SUDO=$COMMAND_SUDO rotate_copies=$rotate_copies backup_path="$backup_path" 'bash -s' << 'ENDSSH' > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" 2>&1 &
2015-11-12 01:26:38 +01:00
function _RemoteLogger {
local value="${1}" # What to log
echo -e "$value"
}
function RemoteLogger {
local value="${1}" # Sentence to log (in double quotes)
local level="${2}" # Log level: PARANOIA_DEBUG, DEBUG, NOTICE, WARN, ERROR, CRITIAL
prefix="REMOTE TIME: $SECONDS - "
if [ "$level" == "CRITICAL" ]; then
_RemoteLogger "$prefix\e[41m$value\e[0m"
return
elif [ "$level" == "ERROR" ]; then
_RemoteLogger "$prefix\e[91m$value\e[0m"
return
elif [ "$level" == "WARN" ]; then
_RemoteLogger "$prefix\e[93m$value\e[0m"
return
elif [ "$level" == "NOTICE" ]; then
_RemoteLogger "$prefix$value"
return
elif [ "$level" == "DEBUG" ]; then
if [ "$_DEBUG" == "yes" ]; then
_RemoteLogger "$prefix$value"
return
fi
else
_RemoteLogger "\e[41mLogger function called without proper loglevel.\e[0m"
_RemoteLogger "$prefix$value"
fi
}
function _RotateBackupsRemoteSSH {
OLD_IFS=$IFS
IFS=$'\t\n'
2015-11-12 01:26:38 +01:00
for backup in $(ls -I "*.$PROGRAM.*" "$backup_path")
2013-07-16 23:10:27 +02:00
do
copy=$rotate_copies
2013-07-16 23:10:27 +02:00
while [ $copy -gt 1 ]
do
if [ $copy -eq $rotate_copies ]; then
2015-11-12 01:26:38 +01:00
cmd="$COMMAND_SUDO rm -rf \"$backup_path/$backup.$PROGRAM.$copy\""
RemoteLogger "cmd: $cmd" "DEBUG"
eval "$cmd"
if [ $? != 0 ]; then
RemoteLogger "Cannot delete oldest copy [$backup_path/$backup.$PROGRAM.$copy]." "ERROR"
fi
2013-07-16 23:10:27 +02:00
fi
2015-11-12 01:26:38 +01:00
path="$backup_path/$backup.$PROGRAM.$(($copy-1))"
if [[ -f $path || -d $path ]]; then
cmd="$COMMAND_SUDO mv \"$path\" \"$backup_path/$backup.$PROGRAM.$copy\""
RemoteLogger "cmd: $cmd" "DEBUG"
eval "$cmd"
if [ $? != 0 ]; then
RemoteLogger "Cannot move [$path] to [$backup_path/$backup.$PROGRAM.$copy]." "ERROR"
fi
2014-09-22 22:45:18 +02:00
2013-07-16 23:10:27 +02:00
fi
copy=$(($copy-1))
done
# Latest file backup will not be moved if script configured for remote backup so next rsync execution will only do delta copy instead of full one
2015-11-12 01:26:38 +01:00
if [[ $backup == *.sql.* ]]; then
cmd="$COMMAND_SUDO mv \"$backup_path/$backup\" \"$backup_path/$backup.$PROGRAM.1\""
RemoteLogger "cmd: $cmd" "DEBUG"
eval "$cmd"
if [ $? != 0 ]; then
RemoteLogger "Cannot move [$backup_path/$backup] to [$backup_path/$backup.$PROGRAM.1]." "ERROR"
fi
2014-09-22 22:45:18 +02:00
2015-11-12 01:26:38 +01:00
elif [ "$REMOTE_OPERATION" == "yes" ]; then
cmd="$COMMAND_SUDO cp -R \"$backup_path/$backup\" \"$backup_path/$backup.$PROGRAM.1\""
RemoteLogger "cmd: $cmd" "DEBUG"
eval "$cmd"
if [ $? != 0 ]; then
RemoteLogger "Cannot copy [$backup_path/$backup] to [$backup_path/$backup.$PROGRAM.1]." "ERROR"
fi
2014-09-22 22:45:18 +02:00
2015-11-12 01:26:38 +01:00
else
cmd="$COMMAND_SUDO mv \"$backup_path/$backup\" \"$backup_path/$backup.$PROGRAM.1\""
RemoteLogger "cmd: $cmd" "DEBUG"
eval "$cmd"
if [ $? != 0 ]; then
RemoteLogger "Cannot move [$backup_path/$backup] to [$backup_path/$backup.$PROGRAM.1]." "ERROR"
fi
2013-07-16 23:10:27 +02:00
fi
done
IFS=$OLD_IFS
2013-07-16 23:10:27 +02:00
}
2015-11-12 01:26:38 +01:00
_RotateBackupsRemoteSSH
2013-07-16 23:10:27 +02:00
2015-11-12 01:26:38 +01:00
ENDSSH
2016-02-19 22:14:13 +01:00
WaitForTaskCompletion $! 1800 0 ${FUNCNAME[0]}
2015-11-12 01:26:38 +01:00
if [ $? != 0 ]; then
Logger "Could not rotate backups in [$backup_path]." "ERROR"
2016-02-19 22:14:13 +01:00
Logger "Command output:\n $(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" "ERROR"
2015-11-12 01:26:38 +01:00
else
Logger "Remote rotation succeed." "NOTICE"
fi ## Need to add a trivial sleep time to give ssh time to log to local file
#sleep 5
}
function RotateBackups {
local backup_path="${1}"
local rotate_copies="${2}"
2015-11-12 01:26:38 +01:00
Logger "Rotating backups." "NOTICE"
if [ "$BACKUP_TYPE" == "local" ] || [ "$BACKUP_TYPE" == "pull" ]; then
_RotateBackupsLocal "$backup_path" "$rotate_copies"
2015-11-12 01:26:38 +01:00
elif [ "$BACKUP_TYPE" == "push" ]; then
_RotateBackupsRemote "$backup_path" "$rotate_copies"
2013-07-16 23:10:27 +02:00
fi
2015-11-12 01:26:38 +01:00
}
2013-07-16 23:10:27 +02:00
2015-11-12 01:26:38 +01:00
function Init {
2013-09-22 12:04:09 +02:00
2016-02-19 22:14:13 +01:00
local uri
local hosturiandpath
local hosturi
trap TrapStop SIGINT SIGQUIT SIGTERM SIGHUP
2015-11-12 01:26:38 +01:00
trap TrapQuit EXIT
2013-07-20 14:48:06 +02:00
2015-11-12 01:26:38 +01:00
## Test if target dir is a ssh uri, and if yes, break it down it its values
if [ "${REMOTE_SYSTEM_URI:0:6}" == "ssh://" ] && [ "$BACKUP_TYPE" != "local" ]; then
REMOTE_OPERATION="yes"
2013-07-20 14:48:06 +02:00
2015-11-12 01:26:38 +01:00
# remove leadng 'ssh://'
uri=${REMOTE_SYSTEM_URI#ssh://*}
if [[ "$uri" == *"@"* ]]; then
# remove everything after '@'
REMOTE_USER=${uri%@*}
2013-09-22 12:04:09 +02:00
else
2015-11-12 01:26:38 +01:00
REMOTE_USER=$LOCAL_USER
fi
2013-07-20 14:48:06 +02:00
2015-11-12 01:26:38 +01:00
if [ "$SSH_RSA_PRIVATE_KEY" == "" ]; then
SSH_RSA_PRIVATE_KEY=~/.ssh/id_rsa
fi
# remove everything before '@'
2016-02-19 22:14:13 +01:00
hosturiandpath=${uri#*@}
2015-11-12 01:26:38 +01:00
# remove everything after first '/'
2016-02-19 22:14:13 +01:00
hosturi=${hosturiandpath%%/*}
if [[ "$hosturi" == *":"* ]]; then
REMOTE_PORT=${hosturi##*:}
2015-11-12 01:26:38 +01:00
else
REMOTE_PORT=22
fi
2016-02-19 22:14:13 +01:00
REMOTE_HOST=${hosturi%%:*}
2015-11-12 01:26:38 +01:00
fi
## Add update to default RSYNC_ARGS
2016-03-01 20:11:38 +01:00
RSYNC_ARGS=$RSYNC_ARGS" -u"
2015-11-12 01:26:38 +01:00
if [ $_VERBOSE -eq 1 ]; then
2016-03-01 20:11:38 +01:00
RSYNC_ARGS=$RSYNC_ARGS" -i"
2013-08-18 11:58:55 +02:00
fi
2015-11-12 01:26:38 +01:00
if [ "$DELETE_VANISHED_FILES" == "yes" ]; then
RSYNC_ARGS=$RSYNC_ARGS" --delete"
2015-01-08 17:16:57 +01:00
fi
2015-11-12 01:26:38 +01:00
if [ $stats -eq 1 ]; then
RSYNC_ARGS=$RSYNC_ARGS" --stats"
fi
2014-09-22 22:45:18 +02:00
2015-11-12 01:26:38 +01:00
## Fix for symlink to directories on target cannot get updated
RSYNC_ARGS=$RSYNC_ARGS" --force"
2014-09-22 22:45:18 +02:00
}
2015-11-12 01:26:38 +01:00
function Main {
2013-07-16 23:10:27 +02:00
2015-11-12 01:26:38 +01:00
if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL -eq 1 ]; then
2013-07-16 23:10:27 +02:00
ListDatabases
fi
2015-11-12 01:26:38 +01:00
if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES -eq 1 ]; then
ListRecursiveBackupDirectories
if [ "$GET_BACKUP_SIZE" != "no" ]; then
GetDirectoriesSize
2015-11-12 01:26:38 +01:00
else
2016-02-19 22:14:13 +01:00
TOTAL_FILES_SIZE=0
fi
2013-07-16 23:10:27 +02:00
fi
2013-08-24 19:41:18 +02:00
2015-11-12 01:26:38 +01:00
if [ "$CREATE_DIRS" != "no" ]; then
CreateStorageDirectories
fi
CheckDiskSpace
2013-07-16 23:10:27 +02:00
2013-08-18 11:58:55 +02:00
# Actual backup process
2015-11-12 01:26:38 +01:00
if [ "$SQL_BACKUP" != "no" ] && [ $CAN_BACKUP_SQL -eq 1 ]; then
if [ $_DRYRUN -ne 1 ] && [ "$ROTATE_SQL_BACKUPS" == "yes" ]; then
RotateBackups "$SQL_STORAGE" "$ROTATE_SQL_COPIES"
2013-07-16 23:10:27 +02:00
fi
2014-11-27 14:40:30 +01:00
BackupDatabases
2013-07-16 23:10:27 +02:00
fi
2013-08-18 11:58:55 +02:00
2015-11-12 01:26:38 +01:00
if [ "$FILE_BACKUP" != "no" ] && [ $CAN_BACKUP_FILES -eq 1 ]; then
if [ $_DRYRUN -ne 1 ] && [ "$ROTATE_FILE_BACKUPS" == "yes" ]; then
RotateBackups "$FILE_STORAGE" "$ROTATE_FILE_COPIES"
2013-07-16 23:10:27 +02:00
fi
2015-11-28 12:48:19 +01:00
## Add Rsync include / exclude patterns
RsyncPatterns
2013-07-16 23:10:27 +02:00
FilesBackup
fi
}
2015-11-12 01:26:38 +01:00
function Usage {
if [ "$IS_STABLE" != "yes" ]; then
echo -e "\e[93mThis is an unstable dev build. Please use with caution.\e[0m"
fi
echo "$PROGRAM $PROGRAM_VERSION $PROGRAM_BUILD"
2014-09-22 22:45:18 +02:00
echo "$AUTHOR"
echo "$CONTACT"
2013-07-16 23:10:27 +02:00
echo ""
2015-11-12 01:26:38 +01:00
echo "usage: obackup.sh /path/to/backup.conf [OPTIONS]"
2013-07-16 23:10:27 +02:00
echo ""
2015-11-12 01:26:38 +01:00
echo "OPTIONS:"
2016-02-16 21:09:57 +01:00
echo "--dry will run obackup without actually doing anything, just testing"
echo "--silent will run obackup without any output to stdout, usefull for cron backups"
echo "--verbose adds command outputs"
2015-11-12 01:26:38 +01:00
echo "--stats Adds rsync transfer statistics to verbose output"
echo "--partial Allows rsync to keep partial downloads that can be resumed later (experimental)"
2015-01-08 17:16:57 +01:00
echo "--no-maxtime disables any soft and hard execution time checks"
echo "--delete Deletes files on destination that vanished on source"
2015-11-12 01:26:38 +01:00
echo "--dontgetsize Does not try to evaluate backup size"
2013-07-16 23:10:27 +02:00
exit 128
}
# Command line argument flags
2015-11-12 01:26:38 +01:00
_DRYRUN=0
_SILENT=0
no_maxtime=0
stats=0
PARTIAL=0
2015-11-12 01:26:38 +01:00
function GetCommandlineArguments {
if [ $# -eq 0 ]; then
2013-07-16 23:10:27 +02:00
Usage
fi
for i in "$@"
do
case $i in
--dry)
2015-11-12 01:26:38 +01:00
_DRYRUN=1
;;
--silent)
2015-11-12 01:26:38 +01:00
_SILENT=1
;;
--verbose)
2015-11-12 01:26:38 +01:00
_VERBOSE=1
;;
--stats)
stats=1
;;
--partial)
PARTIAL="yes"
;;
--no-maxtime)
no_maxtime=1
;;
--delete)
DELETE_VANISHED_FILES="yes"
;;
--dontgetsize)
2015-11-12 01:26:38 +01:00
GET_BACKUP_SIZE="no"
;;
--help|-h|--version|-v)
Usage
;;
esac
done
}
2013-07-16 23:10:27 +02:00
GetCommandlineArguments "$@"
2015-11-12 01:26:38 +01:00
LoadConfigFile "$1"
if [ "$LOGFILE" == "" ]; then
if [ -w /var/log ]; then
LOG_FILE=/var/log/$PROGRAM.$INSTANCE_ID.log
2013-07-16 23:10:27 +02:00
else
2015-11-12 01:26:38 +01:00
LOG_FILE=./$PROGRAM.$INSTANCE_ID.log
2013-07-16 23:10:27 +02:00
fi
else
2015-11-12 01:26:38 +01:00
LOG_FILE="$LOGFILE"
fi
if [ "$IS_STABLE" != "yes" ]; then
Logger "This is an unstable dev build. Please use with caution." "WARN"
fi
GetLocalOS
InitLocalOSSettings
2016-02-19 22:14:13 +01:00
CheckEnvironment
2016-03-03 16:06:27 +01:00
CheckRunningInstances
2015-11-12 01:26:38 +01:00
PreInit
Init
PostInit
CheckCurrentConfig
if [ "$REMOTE_OPERATION" == "yes" ]; then
GetRemoteOS
InitRemoteOSSettings
2013-07-16 23:10:27 +02:00
fi
2015-11-12 01:26:38 +01:00
DATE=$(date)
Logger "--------------------------------------------------------------------" "NOTICE"
Logger "$DRY_WARNING $DATE - $PROGRAM v$PROGRAM_VERSION $BACKUP_TYPE script begin." "NOTICE"
Logger "--------------------------------------------------------------------" "NOTICE"
Logger "Backup instance [$INSTANCE_ID] launched as $LOCAL_USER@$LOCAL_HOST (PID $SCRIPT_PID)" "NOTICE"
if [ $no_maxtime -eq 1 ]; then
SOFT_MAX_EXEC_TIME_DB_TASK=0
SOFT_MAX_EXEC_TIME_FILE_TASK=0
HARD_MAX_EXEC_TIME_DB_TASK=0
HARD_MAX_EXEC_TIME_FILE_TASK=0
HARD_MAX_EXEC_TIME_TOTAL=0
fi
RunBeforeHook
Main
RunAfterHook