Rebuilt targets

This commit is contained in:
deajan 2016-11-10 16:25:37 +01:00
parent 3975763ed2
commit 711465f701
2 changed files with 711 additions and 281 deletions

View File

@ -1,7 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#TODO: missing files says Backup succeed #TODO: missing files says Backup succeed
#TODO: add new encryption variable checks, also upgrade script
#TODO: ListingDatabases fail succeed #TODO: ListingDatabases fail succeed
#TODO: Add .gpg extesion to RotateFiles ? #TODO: Add .gpg extesion to RotateFiles ?
@ -10,12 +9,12 @@ 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=2016090901 PROGRAM_BUILD=2016111002
IS_STABLE=no IS_STABLE=no
#### MINIMAL-FUNCTION-SET BEGIN #### #### MINIMAL-FUNCTION-SET BEGIN ####
## FUNC_BUILD=2016090701 ## FUNC_BUILD=2016111002
## BEGIN Generic bash functions 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: ## To use in a program, define the following variables:
@ -23,6 +22,8 @@ IS_STABLE=no
## INSTANCE_ID=program-instance-name ## INSTANCE_ID=program-instance-name
## _DEBUG=yes/no ## _DEBUG=yes/no
#TODO(high): Refactor GetRemoteOs into big oneliner for faster execution (if busybox else uname else uname -spio in one statement)
#TODO(high): Implement busybox support in SendEmail function
#TODO: Windows checks, check sendmail & mailsend #TODO: Windows checks, check sendmail & mailsend
if ! type "$BASH" > /dev/null; then if ! type "$BASH" > /dev/null; then
@ -98,7 +99,7 @@ fi
# Default alert attachment filename # Default alert attachment filename
ALERT_LOG_FILE="$RUN_DIR/$PROGRAM.last.log" ALERT_LOG_FILE="$RUN_DIR/$PROGRAM.$SCRIPT_PID.last.log"
# Set error exit code if a piped command fails # Set error exit code if a piped command fails
set -o pipefail set -o pipefail
@ -120,7 +121,7 @@ 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 == true ]; then if [ $_LOGGER_STDERR == true ] && [ "$evalue" != "" ]; then
cat <<< "$evalue" 1>&2 cat <<< "$evalue" 1>&2
elif [ "$_SILENT" == false ]; then elif [ "$_SILENT" == false ]; then
echo -e "$svalue" echo -e "$svalue"
@ -314,6 +315,30 @@ function SendAlert {
if [ "$mail_no_attachment" -eq 0 ]; then if [ "$mail_no_attachment" -eq 0 ]; then
attachment_command="-a $ALERT_LOG_FILE" attachment_command="-a $ALERT_LOG_FILE"
fi fi
if [ "$LOCAL_OS" == "BUSYBOX" ]; then
if type sendmail > /dev/null 2>&1; then
if [ "$ENCRYPTION" == "tls" ]; then
echo -e "Subject:$subject\r\n$body" | $(type -p sendmail) -f "$SENDER_MAIL" -H "exec openssl s_client -quiet -tls1_2 -starttls smtp -connect $SMTP_SERVER:$SMTP_PORT" -au"$SMTP_USER" -ap"$SMTP_PASSWORD" $DESTINATION_MAILS
elif [ "$ENCRYPTION" == "ssl" ]; then
echo -e "Subject:$subject\r\n$body" | $(type -p sendmail) -f "$SENDER_MAIL" -H "exec openssl s_client -quiet -connect $SMTP_SERVER:$SMTP_PORT" -au"$SMTP_USER" -ap"$SMTP_PASSWORD" $DESTINATION_MAILS
else
echo -e "Subject:$subject\r\n$body" | $(type -p sendmail) -f "$SENDER_MAIL" -S "$SMTP_SERVER:$SMTP_PORT" -au"$SMTP_USER" -ap"$SMTP_PASSWORD" $DESTINATION_MAILS
fi
if [ $? != 0 ]; then
Logger "Cannot send alert mail via $(type -p sendmail) !!!" "WARN"
# Don't bother try other mail systems with busybox
return 1
else
return 0
fi
else
Logger "Sendmail not present. Won't send any mail" "WARN"
return 1
fi
fi
if type mutt > /dev/null 2>&1 ; then if type mutt > /dev/null 2>&1 ; then
echo "$body" | $(type -p mutt) -x -s "$subject" $DESTINATION_MAILS $attachment_command echo "$body" | $(type -p mutt) -x -s "$subject" $DESTINATION_MAILS $attachment_command
if [ $? != 0 ]; then if [ $? != 0 ]; then
@ -413,7 +438,7 @@ function SendAlert {
# Delete tmp log file # Delete tmp log file
if [ -f "$ALERT_LOG_FILE" ]; then if [ -f "$ALERT_LOG_FILE" ]; then
rm "$ALERT_LOG_FILE" rm -f "$ALERT_LOG_FILE"
fi fi
} }
@ -422,21 +447,21 @@ function SendAlert {
# SendEmail "subject" "Body text" "receiver@example.com receiver2@otherdomain.com" "/path/to/attachment.file" # SendEmail "subject" "Body text" "receiver@example.com receiver2@otherdomain.com" "/path/to/attachment.file"
# Usage (Windows, make sure you have mailsend.exe in executable path, see http://github.com/muquit/mailsend) # Usage (Windows, make sure you have mailsend.exe in executable path, see http://github.com/muquit/mailsend)
# attachment is optional but must be in windows format like "c:\\some\path\\my.file", or "" # attachment is optional but must be in windows format like "c:\\some\path\\my.file", or ""
# smtp_server.domain.tld is mandatory, as is smtp_port (should be 25, 465 or 587) # smtp_server.domain.tld is mandatory, as is smtpPort (should be 25, 465 or 587)
# encryption can be set to tls, ssl or none # encryption can be set to tls, ssl or none
# smtp_user and smtp_password are optional # smtpUser and smtpPassword are optional
# SendEmail "subject" "Body text" "receiver@example.com receiver2@otherdomain.com" "/path/to/attachment.file" "sender_email@example.com" "smtp_server.domain.tld" "smtp_port" "encryption" "smtp_user" "smtp_password" # SendEmail "subject" "Body text" "receiver@example.com receiver2@otherdomain.com" "/path/to/attachment.file" "senderMail@example.com" "smtpServer.domain.tld" "smtpPort" "encryption" "smtpUser" "smtpPassword"
function SendEmail { function SendEmail {
local subject="${1}" local subject="${1}"
local message="${2}" local message="${2}"
local destination_mails="${3}" local destinationMails="${3}"
local attachment="${4}" local attachment="${4}"
local sender_email="${5}" local senderMail="${5}"
local smtp_server="${6}" local smtpServer="${6}"
local smtp_port="${7}" local smtpPort="${7}"
local encryption="${8}" local encryption="${8}"
local smtp_user="${9}" local smtpUser="${9}"
local smtp_password="${10}" local smtpPassword="${10}"
# CheckArguments will report a warning that can be ignored if used in Windows with paranoia debug enabled # CheckArguments will report a warning that can be ignored if used in Windows with paranoia debug enabled
__CheckArguments 4 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 4 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
@ -448,14 +473,37 @@ function SendEmail {
local auth_string= local auth_string=
if [ ! -f "$attachment" ]; then if [ ! -f "$attachment" ]; then
attachment_command="-a $ALERT_LOG_FILE" attachment_command="-a $attachment"
mail_no_attachment=1 mail_no_attachment=1
else else
mail_no_attachment=0 mail_no_attachment=0
fi fi
if [ "$LOCAL_OS" == "BUSYBOX" ]; then
if type sendmail > /dev/null 2>&1; then
if [ "$ENCRYPTION" == "tls" ]; then
echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) -f "$SenderMail" -H "exec openssl s_client -quiet -tls1_2 -starttls smtp -connect $smtpServer:$smtpPort" -au"$smtpUser" -ap"$smtpPassword" "$destinationMails"
elif [ "$ENCRYPTION" == "ssl" ]; then
echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) -f "$SenderMail" -H "exec openssl s_client -quiet -connect $smtpServer:$smtpPort" -au"$smtpUser" -ap"$smtpPassword" "$destinationMails"
else
echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) -f "$SenderMail" -S "$smtpServer:$SmtpPort" -au"$smtpUser" -ap"$smtpPassword" "$destinationMails"
fi
if [ $? != 0 ]; then
Logger "Cannot send alert mail via $(type -p sendmail) !!!" "WARN"
# Don't bother try other mail systems with busybox
return 1
else
return 0
fi
else
Logger "Sendmail not present. Won't send any mail" "WARN"
return 1
fi
fi
if type mutt > /dev/null 2>&1 ; then if type mutt > /dev/null 2>&1 ; then
echo "$message" | $(type -p mutt) -x -s "$subject" "$destination_mails" $attachment_command echo "$message" | $(type -p mutt) -x -s "$subject" "$destinationMails" $attachment_command
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send mail via $(type -p mutt) !!!" "WARN" Logger "Cannot send mail via $(type -p mutt) !!!" "WARN"
else else
@ -472,10 +520,10 @@ function SendEmail {
else else
attachment_command="" attachment_command=""
fi fi
echo "$message" | $(type -p mail) $attachment_command -s "$subject" "$destination_mails" echo "$message" | $(type -p mail) $attachment_command -s "$subject" "$destinationMails"
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send mail via $(type -p mail) with attachments !!!" "WARN" Logger "Cannot send mail via $(type -p mail) with attachments !!!" "WARN"
echo "$message" | $(type -p mail) -s "$subject" "$destination_mails" echo "$message" | $(type -p mail) -s "$subject" "$destinationMails"
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send mail via $(type -p mail) without attachments !!!" "WARN" Logger "Cannot send mail via $(type -p mail) without attachments !!!" "WARN"
else else
@ -489,7 +537,7 @@ function SendEmail {
fi fi
if type sendmail > /dev/null 2>&1 ; then if type sendmail > /dev/null 2>&1 ; then
echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) "$destination_mails" echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) "$destinationMails"
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send mail via $(type -p sendmail) !!!" "WARN" Logger "Cannot send mail via $(type -p sendmail) !!!" "WARN"
else else
@ -500,17 +548,17 @@ function SendEmail {
# Windows specific # Windows specific
if type "mailsend.exe" > /dev/null 2>&1 ; then if type "mailsend.exe" > /dev/null 2>&1 ; then
if [ "$sender_email" == "" ]; then if [ "$senderMail" == "" ]; then
Logger "Missing sender email." "ERROR" Logger "Missing sender email." "ERROR"
return 1 return 1
fi fi
if [ "$smtp_server" == "" ]; then if [ "$smtpServer" == "" ]; then
Logger "Missing smtp port." "ERROR" Logger "Missing smtp port." "ERROR"
return 1 return 1
fi fi
if [ "$smtp_port" == "" ]; then if [ "$smtpPort" == "" ]; then
Logger "Missing smtp port, assuming 25." "WARN" Logger "Missing smtp port, assuming 25." "WARN"
smtp_port=25 smtpPort=25
fi fi
if [ "$encryption" != "tls" ] && [ "$encryption" != "ssl" ] && [ "$encryption" != "none" ]; then if [ "$encryption" != "tls" ] && [ "$encryption" != "ssl" ] && [ "$encryption" != "none" ]; then
Logger "Bogus smtp encryption, assuming none." "WARN" Logger "Bogus smtp encryption, assuming none." "WARN"
@ -520,10 +568,10 @@ function SendEmail {
elif [ "$encryption" == "ssl" ]:; then elif [ "$encryption" == "ssl" ]:; then
encryption_string=-ssl encryption_string=-ssl
fi fi
if [ "$smtp_user" != "" ] && [ "$smtp_password" != "" ]; then if [ "$smtpUser" != "" ] && [ "$smtpPassword" != "" ]; then
auth_string="-auth -user \"$smtp_user\" -pass \"$smtp_password\"" auth_string="-auth -user \"$smtpUser\" -pass \"$smtpPassword\""
fi fi
$(type mailsend.exe) -f "$sender_email" -t "$destination_mails" -sub "$subject" -M "$message" -attach "$attachment" -smtp "$smtp_server" -port "$smtp_port" $encryption_string $auth_string $(type mailsend.exe) -f "$senderMail" -t "$destinationMails" -sub "$subject" -M "$message" -attach "$attachment" -smtp "$smtpServer" -port "$smtpPort" $encryption_string $auth_string
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send mail via $(type mailsend.exe) !!!" "WARN" Logger "Cannot send mail via $(type mailsend.exe) !!!" "WARN"
else else
@ -551,6 +599,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 == false ]; 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
@ -558,6 +607,7 @@ function TrapError {
function LoadConfigFile { function LoadConfigFile {
local configFile="${1}" local configFile="${1}"
__CheckArguments 1 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 1 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
@ -621,13 +671,13 @@ function joinString {
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 softMaxTime="${2}" # If program with pid $pid takes longer than $softMaxTime seconds, will log a warning, unless $softMaxTime 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 hardMaxTime="${3}" # If program with pid $pid takes longer than $hardMaxTime seconds, will stop execution, unless $hardMaxTime equals 0.
local caller_name="${4}" # Who called this function local callerName="${4}" # Who called this function
local counting="${5:-true}" # Count time since function has been launched if true, since script has been launched if false local counting="${5:-true}" # Count time since function has been launched if true, since script has been launched if false
local keep_logging="${6:-0}" # Log a standby message every X seconds. Set to zero to disable logging local keepLogging="${6:-0}" # Log a standby message every X seconds. Set to zero to disable logging
Logger "${FUNCNAME[0]} called by [$caller_name]." "PARANOIA_DEBUG" #__WITH_PARANOIA_DEBUG Logger "${FUNCNAME[0]} called by [$callerName]." "PARANOIA_DEBUG" #__WITH_PARANOIA_DEBUG
__CheckArguments 6 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 6 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
local soft_alert=false # 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
@ -663,8 +713,8 @@ function WaitForTaskCompletion {
exec_time=$SECONDS exec_time=$SECONDS
fi fi
if [ $keep_logging -ne 0 ]; then if [ $keepLogging -ne 0 ]; then
if [ $((($exec_time + 1) % $keep_logging)) -eq 0 ]; then if [ $((($exec_time + 1) % $keepLogging)) -eq 0 ]; then
if [ $log_ttime -ne $exec_time ]; then # Fix when sleep time lower than 1s if [ $log_ttime -ne $exec_time ]; then # Fix when sleep time lower than 1s
log_ttime=$exec_time log_ttime=$exec_time
Logger "Current tasks still running with pids [$(joinString , ${pidsArray[@]})]." "NOTICE" Logger "Current tasks still running with pids [$(joinString , ${pidsArray[@]})]." "NOTICE"
@ -672,15 +722,15 @@ function WaitForTaskCompletion {
fi fi
fi fi
if [ $exec_time -gt $soft_max_time ]; then if [ $exec_time -gt $softMaxTime ]; then
if [ $soft_alert == true ] && [ $soft_max_time -ne 0 ]; then if [ $soft_alert == true ] && [ $softMaxTime -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 [$callerName] with pids [$(joinString , ${pidsArray[@]})]." "WARN"
soft_alert=true soft_alert=true
SendAlert true SendAlert true
fi fi
if [ $exec_time -gt $hard_max_time ] && [ $hard_max_time -ne 0 ]; then if [ $exec_time -gt $hardMaxTime ] && [ $hardMaxTime -ne 0 ]; then
Logger "Max hard execution time exceeded for task [$caller_name] with pids [$(joinString , ${pidsArray[@]})]. Stopping task execution." "ERROR" Logger "Max hard execution time exceeded for task [$callerName] with pids [$(joinString , ${pidsArray[@]})]. Stopping task execution." "ERROR"
for pid in "${pidsArray[@]}"; do for pid in "${pidsArray[@]}"; do
KillChilds $pid true KillChilds $pid true
if [ $? == 0 ]; then if [ $? == 0 ]; then
@ -697,8 +747,10 @@ function WaitForTaskCompletion {
if [ $(IsInteger $pid) -eq 1 ]; then if [ $(IsInteger $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 & busybox.
pidState=$(ps -p$pid -o state= 2 > /dev/null) #TODO(high): propagate changes to ParallelExec
#pidState=$(ps -p$pid -o state= 2 > /dev/null)
pidState="$(eval $PROCESS_STATE_CMD)"
if [ "$pidState" != "D" ] && [ "$pidState" != "Z" ]; then if [ "$pidState" != "D" ] && [ "$pidState" != "Z" ]; then
newPidsArray+=($pid) newPidsArray+=($pid)
fi fi
@ -708,7 +760,7 @@ 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 [$retval]." "DEBUG" Logger "${FUNCNAME[0]} called by [$callerName] finished monitoring [$pid] with exitcode [$retval]." "DEBUG"
if [ "$WAIT_FOR_TASK_COMPLETION" == "" ]; then if [ "$WAIT_FOR_TASK_COMPLETION" == "" ]; then
WAIT_FOR_TASK_COMPLETION="$pid:$retval" WAIT_FOR_TASK_COMPLETION="$pid:$retval"
else else
@ -729,7 +781,7 @@ function WaitForTaskCompletion {
sleep $SLEEP_TIME sleep $SLEEP_TIME
done done
Logger "${FUNCNAME[0]} ended for [$caller_name] using [$pidCount] subprocesses with [$errorcount] errors." "PARANOIA_DEBUG" #__WITH_PARANOIA_DEBUG Logger "${FUNCNAME[0]} ended for [$callerName] using [$pidCount] subprocesses with [$errorcount] errors." "PARANOIA_DEBUG" #__WITH_PARANOIA_DEBUG
# Return exit code if only one process was monitored, else return number of errors # Return exit code if only one process was monitored, else return number of errors
if [ $pidCount -eq 1 ] && [ $errorcount -eq 0 ]; then if [ $pidCount -eq 1 ] && [ $errorcount -eq 0 ]; then
@ -746,8 +798,13 @@ function ParallelExec {
local numberOfProcesses="${1}" # Number of simultaneous commands to run local numberOfProcesses="${1}" # Number of simultaneous commands to run
local commandsArg="${2}" # Semi-colon separated list of commands, or file containing one command per line local commandsArg="${2}" # Semi-colon separated list of commands, or file containing one command per line
local readFromFile="${3:-false}" # Is commandsArg a file or a string ? local readFromFile="${3:-false}" # Is commandsArg a file or a string ?
local softMaxTime="${4:-0}"
local hardMaxTime="${5:-0}"
local callerName="${6}" # Who called this function
local counting="${7:-true}" # Count time since function has been launched if true, since script has been launched if false
local keepLogging="${8:-0}" # Log a standby message every X seconds. Set to zero to disable logging
__CheckArguments 3 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 8 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
local commandCount local commandCount
local command local command
@ -786,7 +843,7 @@ function ParallelExec {
command="${commandsArray[$counter]}" command="${commandsArray[$counter]}"
fi fi
Logger "Running command [$command]." "DEBUG" Logger "Running command [$command]." "DEBUG"
eval "$command" & eval "$command" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" 2>&1 &
pid=$! pid=$!
pidsArray+=($pid) pidsArray+=($pid)
commandsArrayPid[$pid]="$command" commandsArrayPid[$pid]="$command"
@ -799,7 +856,8 @@ function ParallelExec {
if [ $(IsInteger $pid) -eq 1 ]; then if [ $(IsInteger $pid) -eq 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 ? :)
if kill -0 $pid > /dev/null 2>&1; then if kill -0 $pid > /dev/null 2>&1; then
pidState=$(ps -p$pid -o state= 2 > /dev/null) #pidState=$(ps -p$pid -o state= 2 > /dev/null)
pidState="$(eval $PROCESS_STATE_CMD)"
if [ "$pidState" != "D" ] && [ "$pidState" != "Z" ]; then if [ "$pidState" != "D" ] && [ "$pidState" != "Z" ]; then
newPidsArray+=($pid) newPidsArray+=($pid)
fi fi
@ -838,8 +896,6 @@ function CleanUp {
fi fi
} }
#### MINIMAL-FUNCTION-SET END ####
# obsolete, use StripQuotes # obsolete, use StripQuotes
function SedStripQuotes { function SedStripQuotes {
echo $(echo $1 | sed "s/^\([\"']\)\(.*\)\1\$/\2/g") echo $(echo $1 | sed "s/^\([\"']\)\(.*\)\1\$/\2/g")
@ -848,6 +904,7 @@ function SedStripQuotes {
# Usage: var=$(StripSingleQuotes "$var") # Usage: var=$(StripSingleQuotes "$var")
function StripSingleQuotes { function StripSingleQuotes {
local string="${1}" local string="${1}"
string="${string/#\'/}" # Remove singlequote if it begins string string="${string/#\'/}" # Remove singlequote if it begins string
string="${string/%\'/}" # Remove singlequote if it ends string string="${string/%\'/}" # Remove singlequote if it ends string
echo "$string" echo "$string"
@ -856,6 +913,7 @@ function StripSingleQuotes {
# Usage: var=$(StripDoubleQuotes "$var") # Usage: var=$(StripDoubleQuotes "$var")
function StripDoubleQuotes { function StripDoubleQuotes {
local string="${1}" local string="${1}"
string="${string/#\"/}" string="${string/#\"/}"
string="${string/%\"/}" string="${string/%\"/}"
echo "$string" echo "$string"
@ -863,12 +921,14 @@ function StripDoubleQuotes {
function StripQuotes { function StripQuotes {
local string="${1}" local string="${1}"
echo "$(StripSingleQuotes $(StripDoubleQuotes $string))" echo "$(StripSingleQuotes $(StripDoubleQuotes $string))"
} }
# Usage var=$(EscapeSpaces "$var") or var="$(EscapeSpaces "$var")" # Usage var=$(EscapeSpaces "$var") or var="$(EscapeSpaces "$var")"
function EscapeSpaces { function EscapeSpaces {
local string="${1}" # String on which spaces will be escaped local string="${1}" # String on which spaces will be escaped
echo "${string// /\\ }" echo "${string// /\\ }"
} }
@ -876,20 +936,22 @@ function IsNumericExpand {
eval "local value=\"${1}\"" # Needed eval so variable variables can be processed eval "local value=\"${1}\"" # Needed eval so variable variables can be processed
local re="^-?[0-9]+([.][0-9]+)?$" local re="^-?[0-9]+([.][0-9]+)?$"
if [[ $value =~ $re ]]; then if [[ $value =~ $re ]]; then
echo 1 echo 1 && return 1
else else
echo 0 echo 0 && return 0
fi fi
} }
# Usage [ $(IsNumeric $var) -eq 1 ]
function IsNumeric { function IsNumeric {
local value="${1}" local value="${1}"
if [[ $value =~ ^[0-9]+([.][0-9]+)?$ ]]; then if [[ $value =~ ^[0-9]+([.][0-9]+)?$ ]]; then
echo 1 echo 1 && return 1
else else
echo 0 echo 0 && return 0
fi fi
} }
@ -897,12 +959,43 @@ function IsInteger {
local value="${1}" local value="${1}"
if [[ $value =~ ^[0-9]+$ ]]; then if [[ $value =~ ^[0-9]+$ ]]; then
echo 1 echo 1 && return 1
else else
echo 0 echo 0 && return 0
fi fi
} }
# Converts human readable sizes into integer kilobyte sizes
# Usage numericSize="$(HumanToNumeric $humanSize)"
function HumanToNumeric {
local value="${1}"
local notation
local suffix
local suffixPresent
local multiplier
notation=(K M G T P E)
for suffix in "${notation[@]}"; do
multiplier=$((multiplier+1))
if [[ "$value" == *"$suffix"* ]]; then
suffixPresent=$suffix
break;
fi
done
if [ "$suffixPresent" != "" ]; then
value=${value%$suffix*}
value=${value%.*}
# /1024 since we convert to kilobytes instead of bytes
value=$((value*(1024**multiplier/1024)))
else
value=${value%.*}
fi
echo $value
}
## from https://gist.github.com/cdown/1163649 ## from https://gist.github.com/cdown/1163649
function urlEncode { function urlEncode {
local length="${#1}" local length="${#1}"
@ -922,25 +1015,46 @@ function urlEncode {
} }
function urlDecode { function urlDecode {
local url_encoded="${1//+/ }" local urlEncoded="${1//+/ }"
printf '%b' "${url_encoded//%/\\x}" printf '%b' "${urlEncoded//%/\\x}"
}
## Modified version of http://stackoverflow.com/a/8574392
## Usage: arrayContains "needle" "${haystack[@]}"
arrayContains () {
local e
if [ "$2" == "" ]; then
echo 0 && return 0
fi
for e in "${@:2}"; do
[[ "$e" == "$1" ]] && echo 1 && return 1
done
echo 0 && return 0
} }
function GetLocalOS { function GetLocalOS {
__CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
local local_os_var= local localOsVar
local_os_var="$(uname -spio 2>&1)" # There's no good way to tell if currently running in BusyBox shell. Using sluggish way.
ls --help 2>&1 | grep -i BusyBox > /dev/null
if [ $? == 0 ]; then
localOsVar="BusyBox"
else
localOsVar="$(uname -spio 2>&1)"
if [ $? != 0 ]; then if [ $? != 0 ]; then
local_os_var="$(uname -v 2>&1)" localOsVar="$(uname -v 2>&1)"
if [ $? != 0 ]; then if [ $? != 0 ]; then
local_os_var="$(uname)" localOsVar="$(uname)"
fi
fi fi
fi fi
case $local_os_var in case $localOsVar in
*"Linux"*) *"Linux"*)
LOCAL_OS="Linux" LOCAL_OS="Linux"
;; ;;
@ -953,28 +1067,42 @@ function GetLocalOS {
*"Darwin"*) *"Darwin"*)
LOCAL_OS="MacOSX" LOCAL_OS="MacOSX"
;; ;;
*"BusyBox"*)
LOCAL_OS="BUSYBOX"
;;
*) *)
if [ "$IGNORE_OS_TYPE" == "yes" ]; then #DOC: Undocumented option if [ "$IGNORE_OS_TYPE" == "yes" ]; then #TODO(doc): Undocumented option
Logger "Running on unknown local OS [$local_os_var]." "WARN" Logger "Running on unknown local OS [$localOsVar]." "WARN"
return return
fi fi
Logger "Running on >> $local_os_var << not supported. Please report to the author." "ERROR" Logger "Running on >> $localOsVar << not supported. Please report to the author." "ERROR"
exit 1 exit 1
;; ;;
esac esac
Logger "Local OS: [$local_os_var]." "DEBUG" Logger "Local OS: [$localOsVar]." "DEBUG"
} }
#### MINIMAL-FUNCTION-SET END ####
function GetRemoteOS { function GetRemoteOS {
__CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
local cmd= local retval
local remote_os_var= local cmd
local remoteOsVar
if [ "$REMOTE_OPERATION" == "yes" ]; then if [ "$REMOTE_OPERATION" == "yes" ]; then
CheckConnectivity3rdPartyHosts CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost CheckConnectivityRemoteHost
cmd=$SSH_CMD' "ls --help 2>&1 | grep -i BusyBox > /dev/null"'
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
WaitForTaskCompletion $! 120 240 ${FUNCNAME[0]}"-0" true $KEEP_LOGGING
retval=$?
if [ $retval == 0 ]; then
remoteOsVar="BusyBox"
else
cmd=$SSH_CMD' "uname -spio" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1' cmd=$SSH_CMD' "uname -spio" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1'
Logger "cmd: $cmd" "DEBUG" Logger "cmd: $cmd" "DEBUG"
eval "$cmd" & eval "$cmd" &
@ -997,10 +1125,10 @@ function GetRemoteOS {
fi fi
fi fi
fi fi
remoteOsVar=$(cat "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID")
fi
remote_os_var=$(cat "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID") case $remoteOsVar in
case $remote_os_var in
*"Linux"*) *"Linux"*)
REMOTE_OS="Linux" REMOTE_OS="Linux"
;; ;;
@ -1013,27 +1141,30 @@ function GetRemoteOS {
*"Darwin"*) *"Darwin"*)
REMOTE_OS="MacOSX" REMOTE_OS="MacOSX"
;; ;;
*"BusyBox"*)
REMOTE_OS="BUSYBOX"
;;
*"ssh"*|*"SSH"*) *"ssh"*|*"SSH"*)
Logger "Cannot connect to remote system." "CRITICAL" Logger "Cannot connect to remote system." "CRITICAL"
exit 1 exit 1
;; ;;
*) *)
if [ "$IGNORE_OS_TYPE" == "yes" ]; then #DOC: Undocumented option if [ "$IGNORE_OS_TYPE" == "yes" ]; then #DOC: Undocumented option
Logger "Running on unknown remote OS [$remote_os_var]." "WARN" Logger "Running on unknown remote OS [$remoteOsVar]." "WARN"
return return
fi fi
Logger "Running on remote OS failed. Please report to the author if the OS is not supported." "CRITICAL" 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" Logger "Remote OS said:\n$remoteOsVar" "CRITICAL"
exit 1 exit 1
esac esac
Logger "Remote OS: [$remote_os_var]." "DEBUG" Logger "Remote OS: [$remoteOsVar]." "DEBUG"
fi fi
} }
function RunLocalCommand { 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 hardMaxTime="${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 == true ]; then if [ $_DRYRUN == true ]; then
@ -1043,7 +1174,7 @@ function RunLocalCommand {
Logger "Running command [$command] on local host." "NOTICE" Logger "Running command [$command] on local host." "NOTICE"
eval "$command" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" 2>&1 & eval "$command" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" 2>&1 &
WaitForTaskCompletion $! 0 $hard_max_time ${FUNCNAME[0]} true $KEEP_LOGGING WaitForTaskCompletion $! 0 $hardMaxTime ${FUNCNAME[0]} true $KEEP_LOGGING
retval=$? retval=$?
if [ $retval -eq 0 ]; then if [ $retval -eq 0 ]; then
Logger "Command succeded." "NOTICE" Logger "Command succeded." "NOTICE"
@ -1064,7 +1195,7 @@ function RunLocalCommand {
## Runs remote command $1 and waits for completition in $2 seconds ## Runs remote command $1 and waits for completition in $2 seconds
function RunRemoteCommand { function RunRemoteCommand {
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 hardMaxTime="${2}" # Max time to wait for command to compleet
__CheckArguments 2 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 2 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
CheckConnectivity3rdPartyHosts CheckConnectivity3rdPartyHosts
@ -1078,7 +1209,7 @@ function RunRemoteCommand {
cmd=$SSH_CMD' "$command" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1' cmd=$SSH_CMD' "$command" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1'
Logger "cmd: $cmd" "DEBUG" Logger "cmd: $cmd" "DEBUG"
eval "$cmd" & eval "$cmd" &
WaitForTaskCompletion $! 0 $hard_max_time ${FUNCNAME[0]} true $KEEP_LOGGING WaitForTaskCompletion $! 0 $hardMaxTime ${FUNCNAME[0]} true $KEEP_LOGGING
retval=$? retval=$?
if [ $retval -eq 0 ]; then if [ $retval -eq 0 ]; then
Logger "Command succeded." "NOTICE" Logger "Command succeded." "NOTICE"
@ -1100,7 +1231,7 @@ function RunRemoteCommand {
function RunBeforeHook { function RunBeforeHook {
__CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
local pids= local pids
if [ "$LOCAL_RUN_BEFORE_CMD" != "" ]; then if [ "$LOCAL_RUN_BEFORE_CMD" != "" ]; then
RunLocalCommand "$LOCAL_RUN_BEFORE_CMD" $MAX_EXEC_TIME_PER_CMD_BEFORE & RunLocalCommand "$LOCAL_RUN_BEFORE_CMD" $MAX_EXEC_TIME_PER_CMD_BEFORE &
@ -1147,7 +1278,7 @@ function CheckConnectivityRemoteHost {
WaitForTaskCompletion $! 60 180 ${FUNCNAME[0]} true $KEEP_LOGGING WaitForTaskCompletion $! 60 180 ${FUNCNAME[0]} true $KEEP_LOGGING
retval=$? retval=$?
if [ $retval != 0 ]; then if [ $retval != 0 ]; then
Logger "Cannot ping [$REMOTE_HOST]. Return code [$retval]." "ERROR" Logger "Cannot ping [$REMOTE_HOST]. Return code [$retval]." "WARN"
return $retval return $retval
fi fi
fi fi
@ -1157,13 +1288,13 @@ function CheckConnectivityRemoteHost {
function CheckConnectivity3rdPartyHosts { function CheckConnectivity3rdPartyHosts {
__CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
local remote_3rd_party_success local remote3rdPartySuccess
local retval local retval
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=false remote3rdPartySuccess=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" &
@ -1172,12 +1303,12 @@ function CheckConnectivity3rdPartyHosts {
if [ $retval != 0 ]; then if [ $retval != 0 ]; then
Logger "Cannot ping 3rd party host [$i]. Return code [$retval]." "NOTICE" Logger "Cannot ping 3rd party host [$i]. Return code [$retval]." "NOTICE"
else else
remote_3rd_party_success=true remote3rdPartySuccess=true
fi fi
done done
if [ $remote_3rd_party_success == false ]; then if [ $remote3rdPartySuccess == 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 ?" "WARN"
return 1 return 1
else else
return 0 return 0
@ -1239,7 +1370,7 @@ function __CheckArguments {
#__END_WITH_PARANOIA_DEBUG #__END_WITH_PARANOIA_DEBUG
function RsyncPatternsAdd { function RsyncPatternsAdd {
local pattern_type="${1}" # exclude or include local patternType="${1}" # exclude or include
local pattern="${2}" local pattern="${2}"
__CheckArguments 2 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 2 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
@ -1251,7 +1382,7 @@ function RsyncPatternsAdd {
while [ -n "$rest" ] while [ -n "$rest" ]
do do
# Take the string until first occurence until $PATH_SEPARATOR_CHAR # Take the string until first occurence until $PATH_SEPARATOR_CHAR
str=${rest%%;*} str=${rest%%;*} #TODO: replace ; with $PATH_SEPARATOR_CHAR
# Handle the last case # Handle the last case
if [ "$rest" = "${rest/$PATH_SEPARATOR_CHAR/}" ]; then if [ "$rest" = "${rest/$PATH_SEPARATOR_CHAR/}" ]; then
rest= rest=
@ -1260,26 +1391,26 @@ function RsyncPatternsAdd {
rest=${rest#*$PATH_SEPARATOR_CHAR} rest=${rest#*$PATH_SEPARATOR_CHAR}
fi fi
if [ "$RSYNC_PATTERNS" == "" ]; then if [ "$RSYNC_PATTERNS" == "" ]; then
RSYNC_PATTERNS="--"$pattern_type"=\"$str\"" RSYNC_PATTERNS="--"$patternType"=\"$str\""
else else
RSYNC_PATTERNS="$RSYNC_PATTERNS --"$pattern_type"=\"$str\"" RSYNC_PATTERNS="$RSYNC_PATTERNS --"$patternType"=\"$str\""
fi fi
done done
set +f set +f
} }
function RsyncPatternsFromAdd { function RsyncPatternsFromAdd {
local pattern_type="${1}" local patternType="${1}"
local pattern_from="${2}" local patternFrom="${2}"
__CheckArguments 2 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 2 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
## Check if the exclude list has a full path, and if not, add the config file path if there is one ## 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 if [ "$(basename $patternFrom)" == "$patternFrom" ]; then
pattern_from="$(dirname $CONFIG_FILE)/$pattern_from" patternFrom="$(dirname $CONFIG_FILE)/$patternFrom"
fi fi
if [ -e "$pattern_from" ]; then if [ -e "$patternFrom" ]; then
RSYNC_PATTERNS="$RSYNC_PATTERNS --"$pattern_type"-from=\"$pattern_from\"" RSYNC_PATTERNS="$RSYNC_PATTERNS --"$patternType"-from=\"$patternFrom\""
fi fi
} }
@ -1299,7 +1430,8 @@ function RsyncPatterns {
if [ "$RSYNC_INCLUDE_FROM" != "" ]; then if [ "$RSYNC_INCLUDE_FROM" != "" ]; then
RsyncPatternsFromAdd "include" "$RSYNC_INCLUDE_FROM" RsyncPatternsFromAdd "include" "$RSYNC_INCLUDE_FROM"
fi fi
elif [ "$RSYNC_PATTERN_FIRST" == "include" ]; then # Use default include first for quicksync runs
elif [ "$RSYNC_PATTERN_FIRST" == "include" ] || [ "$_QUICK_SYNC" == "2" ]; then
if [ "$RSYNC_INCLUDE_PATTERN" != "" ]; then if [ "$RSYNC_INCLUDE_PATTERN" != "" ]; then
RsyncPatternsAdd "include" "$RSYNC_INCLUDE_PATTERN" RsyncPatternsAdd "include" "$RSYNC_INCLUDE_PATTERN"
fi fi
@ -1320,6 +1452,8 @@ function RsyncPatterns {
function PreInit { function PreInit {
__CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
local compressionString
## SSH compression ## SSH compression
if [ "$SSH_COMPRESSION" != "no" ]; then if [ "$SSH_COMPRESSION" != "no" ]; then
SSH_COMP=-C SSH_COMP=-C
@ -1410,24 +1544,42 @@ function PreInit {
fi fi
## Set compression executable and extension ## Set compression executable and extension
if [ "$(IsInteger $COMPRESSION_LEVEL)" -eq 0 ]; then
COMPRESSION_LEVEL=3 COMPRESSION_LEVEL=3
fi
## Busybox fix (Termux xz command doesn't support compression at all)
if [ "$LOCAL_OS" == "BUSYBOX" ] || [ "$REMOTE_OS" == "BUSYBOX" ]; then
compressionString=""
if type gzip > /dev/null 2>&1
then
COMPRESSION_PROGRAM="| gzip -c$compressionString"
COMPRESSION_EXTENSION=.gz
# obackup specific
else
COMPRESSION_PROGRAM=
COMPRESSION_EXTENSION=
fi
else
compressionString=" -$COMPRESSION_LEVEL"
if type xz > /dev/null 2>&1 if type xz > /dev/null 2>&1
then then
COMPRESSION_PROGRAM="| xz -$COMPRESSION_LEVEL" COMPRESSION_PROGRAM="| xz -c$compressionString"
COMPRESSION_EXTENSION=.xz COMPRESSION_EXTENSION=.xz
elif type lzma > /dev/null 2>&1 elif type lzma > /dev/null 2>&1
then then
COMPRESSION_PROGRAM="| lzma -$COMPRESSION_LEVEL" COMPRESSION_PROGRAM="| lzma -c$compressionString"
COMPRESSION_EXTENSION=.lzma COMPRESSION_EXTENSION=.lzma
elif type pigz > /dev/null 2>&1 elif type pigz > /dev/null 2>&1
then then
COMPRESSION_PROGRAM="| pigz -$COMPRESSION_LEVEL" COMPRESSION_PROGRAM="| pigz -c$compressionString"
COMPRESSION_EXTENSION=.gz COMPRESSION_EXTENSION=.gz
# obackup specific # obackup specific
COMPRESSION_OPTIONS=--rsyncable COMPRESSION_OPTIONS=--rsyncable
elif type gzip > /dev/null 2>&1 elif type gzip > /dev/null 2>&1
then then
COMPRESSION_PROGRAM="| gzip -$COMPRESSION_LEVEL" COMPRESSION_PROGRAM="| gzip -c$compressionString"
COMPRESSION_EXTENSION=.gz COMPRESSION_EXTENSION=.gz
# obackup specific # obackup specific
COMPRESSION_OPTIONS=--rsyncable COMPRESSION_OPTIONS=--rsyncable
@ -1435,6 +1587,7 @@ function PreInit {
COMPRESSION_PROGRAM= COMPRESSION_PROGRAM=
COMPRESSION_EXTENSION= COMPRESSION_EXTENSION=
fi fi
fi
ALERT_LOG_FILE="$ALERT_LOG_FILE$COMPRESSION_EXTENSION" ALERT_LOG_FILE="$ALERT_LOG_FILE$COMPRESSION_EXTENSION"
} }
@ -1442,9 +1595,20 @@ function PostInit {
__CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
# Define remote commands # Define remote commands
if [ -f "$SSH_RSA_PRIVATE_KEY" ]; then
SSH_CMD="$(type -p ssh) $SSH_COMP -i $SSH_RSA_PRIVATE_KEY $SSH_OPTS $REMOTE_USER@$REMOTE_HOST -p $REMOTE_PORT" SSH_CMD="$(type -p ssh) $SSH_COMP -i $SSH_RSA_PRIVATE_KEY $SSH_OPTS $REMOTE_USER@$REMOTE_HOST -p $REMOTE_PORT"
SCP_CMD="$(type -p scp) $SSH_COMP -i $SSH_RSA_PRIVATE_KEY -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 $SSH_OPTS -p $REMOTE_PORT" RSYNC_SSH_CMD="$(type -p ssh) $SSH_COMP -i $SSH_RSA_PRIVATE_KEY $SSH_OPTS -p $REMOTE_PORT"
elif [ -f "$SSH_PASSWORD_FILE" ]; then
SSH_CMD="$(type -p sshpass) -f $SSH_PASSWORD_FILE $(type -p ssh) $SSH_COMP $SSH_OPTS $REMOTE_USER@$REMOTE_HOST -p $REMOTE_PORT"
SCP_CMD="$(type -p sshpass) -f $SSH_PASSWORD_FILE $(type -p scp) $SSH_COMP -P $REMOTE_PORT"
RSYNC_SSH_CMD="$(type -p sshpass) -f $SSH_PASSWORD_FILE $(type -p ssh) $SSH_COMP $SSH_OPTS -p $REMOTE_PORT"
else
SSH_PASSWORD=""
SSH_CMD=""
SCP_CMD=""
RSYNC_SSH_CMD=""
fi
} }
function InitLocalOSSettings { function InitLocalOSSettings {
@ -1462,12 +1626,20 @@ function InitLocalOSSettings {
PING_CMD="ping -c 2 -i .2" PING_CMD="ping -c 2 -i .2"
fi fi
if [ "$LOCAL_OS" == "BUSYBOX" ]; then
PROCESS_STATE_CMD="echo none"
else
PROCESS_STATE_CMD='ps -p$pid -o state= 2 > /dev/null'
fi
## Stat command has different syntax on Linux and FreeBSD/MacOSX ## Stat command has different syntax on Linux and FreeBSD/MacOSX
if [ "$LOCAL_OS" == "MacOSX" ] || [ "$LOCAL_OS" == "BSD" ]; then if [ "$LOCAL_OS" == "MacOSX" ] || [ "$LOCAL_OS" == "BSD" ]; then
# Tested on BSD and Mac
STAT_CMD="stat -f \"%Sm\"" STAT_CMD="stat -f \"%Sm\""
STAT_CTIME_MTIME_CMD="stat -f %N;%c;%m" STAT_CTIME_MTIME_CMD="stat -f %N;%c;%m"
else else
STAT_CMD="stat --format %y" # Tested on GNU stat and busybox
STAT_CMD="stat -c %y"
STAT_CTIME_MTIME_CMD="stat -c %n;%Z;%Y" STAT_CTIME_MTIME_CMD="stat -c %n;%Z;%Y"
fi fi
} }
@ -1504,6 +1676,19 @@ function PrintIFS {
printf "IFS is: %q" "$IFS" printf "IFS is: %q" "$IFS"
} }
# Process debugging
# Recursive function to get all parents from a pid
function ParentPid {
local pid="${1}" # Pid to analyse
local parent
parent=$(ps -p $pid -o ppid=)
echo "$pid is a child of $parent"
if [ $parent -gt 0 ]; then
ParentPid $parent
fi
}
## END Generic functions ## END Generic functions
_LOGGER_PREFIX="time" _LOGGER_PREFIX="time"
@ -1586,6 +1771,11 @@ function CheckEnvironment {
CAN_BACKUP_SQL=false CAN_BACKUP_SQL=false
fi fi
fi fi
if [ "$SSH_PASSWORD_FILE" != "" ] && ! type sshpass > /dev/null 2>&1 ; then
Logger "sshpass not present. Cannot use password authentication." "CRITICAL"
exit 1
fi
fi fi
if [ "$FILE_BACKUP" != "no" ]; then if [ "$FILE_BACKUP" != "no" ]; then
@ -1625,7 +1815,7 @@ function CheckCurrentConfig {
# Check all variables that should contain "yes" or "no" # 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 SSH_COMPRESSION SSH_IGNORE_KNOWN_HOSTS REMOTE_HOST_PING SUDO_EXEC DATABASES_ALL PRESERVE_PERMISSIONS PRESERVE_OWNER PRESERVE_GROUP PRESERVE_EXECUTABILITY 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 RUN_AFTER_CMD_ON_ERROR) declare -a yes_no_vars=(SQL_BACKUP FILE_BACKUP ENCRYPTION CREATE_DIRS KEEP_ABSOLUTE_PATHS GET_BACKUP_SIZE SSH_COMPRESSION SSH_IGNORE_KNOWN_HOSTS REMOTE_HOST_PING SUDO_EXEC DATABASES_ALL PRESERVE_PERMISSIONS PRESERVE_OWNER PRESERVE_GROUP PRESERVE_EXECUTABILITY 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 RUN_AFTER_CMD_ON_ERROR)
for i in "${yes_no_vars[@]}"; do for i in "${yes_no_vars[@]}"; do
test="if [ \"\$$i\" != \"yes\" ] && [ \"\$$i\" != \"no\" ]; then Logger \"Bogus $i value defined in config file. Correct your config file or update it with the update script if using and old version.\" \"CRITICAL\"; exit 1; fi" test="if [ \"\$$i\" != \"yes\" ] && [ \"\$$i\" != \"no\" ]; then Logger \"Bogus $i value [$$i] defined in config file. Correct your config file or update it with the update script if using and old version.\" \"CRITICAL\"; exit 1; fi"
eval "$test" eval "$test"
done done
@ -1637,7 +1827,7 @@ function CheckCurrentConfig {
# Check all variables that should contain a numerical value >= 0 # Check all variables that should contain a numerical value >= 0
declare -a num_vars=(BACKUP_SIZE_MINIMUM 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 BANDWIDTH SOFT_MAX_EXEC_TIME_TOTAL HARD_MAX_EXEC_TIME_TOTAL ROTATE_SQL_COPIES ROTATE_FILE_COPIES KEEP_LOGGING MAX_EXEC_TIME_PER_CMD_BEFORE MAX_EXEC_TIME_PER_CMD_AFTER) declare -a num_vars=(BACKUP_SIZE_MINIMUM 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 BANDWIDTH SOFT_MAX_EXEC_TIME_TOTAL HARD_MAX_EXEC_TIME_TOTAL ROTATE_SQL_COPIES ROTATE_FILE_COPIES KEEP_LOGGING MAX_EXEC_TIME_PER_CMD_BEFORE MAX_EXEC_TIME_PER_CMD_AFTER)
for i in "${num_vars[@]}"; do for i in "${num_vars[@]}"; do
test="if [ $(IsNumericExpand \"\$$i\") -eq 0 ]; then Logger \"Bogus $i value defined in config file. Correct your config file or update it with the update script if using and old version.\" \"CRITICAL\"; exit 1; fi" test="if [ $(IsNumericExpand \"\$$i\") -eq 0 ]; then Logger \"Bogus $i value [$$i] defined in config file. Correct your config file or update it with the update script if using and old version.\" \"CRITICAL\"; exit 1; fi"
eval "$test" eval "$test"
done done
@ -1681,7 +1871,10 @@ function CheckCurrentConfig {
fi fi
fi fi
if [ "$REMOTE_OPERATION" == "yes" ] && ([ ! -f "$SSH_RSA_PRIVATE_KEY" ] && [ ! -f "$SSH_PASSWORD_FILE" ]); then
Logger "Cannot find rsa private key [$SSH_RSA_PRIVATE_KEY] nor password file [$SSH_PASSWORD_FILE]. No authentication method provided." "CRITICAL"
exit 1
fi
} }
function CheckRunningInstances { function CheckRunningInstances {
@ -1883,9 +2076,7 @@ function ListRecursiveBackupDirectories {
local output_file local output_file
local file_exclude local file_exclude
local excluded local excluded
local fileArray local fileArray
Logger "Listing directories to backup." "NOTICE" Logger "Listing directories to backup." "NOTICE"
@ -1976,6 +2167,9 @@ function _GetDirectoriesSizeLocal {
if [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then if [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then
TOTAL_FILES_SIZE="$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" TOTAL_FILES_SIZE="$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)"
if [ $(IsInteger $TOTAL_FILES_SIZE) -eq 0 ]; then
TOTAL_FILES_SIZE="$(HumanToNumeric $TOTAL_FILES_SIZE)"
fi
else else
TOTAL_FILES_SIZE=-1 TOTAL_FILES_SIZE=-1
fi fi
@ -2006,6 +2200,9 @@ function _GetDirectoriesSizeRemote {
fi fi
if [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then if [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then
TOTAL_FILES_SIZE="$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" TOTAL_FILES_SIZE="$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)"
if [ $(IsInteger $TOTAL_FILES_SIZE) -eq 0 ]; then
TOTAL_FILES_SIZE="$(HumanToNumeric $TOTAL_FILES_SIZE)"
fi
else else
TOTAL_FILES_SIZE=-1 TOTAL_FILES_SIZE=-1
fi fi
@ -2116,7 +2313,7 @@ function GetDiskSpaceLocal {
if [ -d "$path_to_check" ]; then if [ -d "$path_to_check" ]; then
# Not elegant solution to make df silent on errors # 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 # No sudo on local commands, assuming you should have all the necesarry rights to check backup directories sizes
df -P "$path_to_check" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" 2>&1 df "$path_to_check" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" 2>&1
if [ $? != 0 ]; then if [ $? != 0 ]; then
DISK_SPACE=0 DISK_SPACE=0
Logger "Cannot get disk space in [$path_to_check] on local system." "ERROR" Logger "Cannot get disk space in [$path_to_check] on local system." "ERROR"
@ -2124,6 +2321,9 @@ function GetDiskSpaceLocal {
else else
DISK_SPACE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $4}') DISK_SPACE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $4}')
DRIVE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $1}') DRIVE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $1}')
if [ $(IsInteger $DISK_SPACE) -eq 0 ]; then
DISK_SPACE="$(HumanToNumeric $DISK_SPACE)"
fi
fi fi
else else
Logger "Storage path [$path_to_check] does not exist." "CRITICAL" Logger "Storage path [$path_to_check] does not exist." "CRITICAL"
@ -2138,7 +2338,7 @@ function GetDiskSpaceRemote {
local cmd 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' cmd=$SSH_CMD' "if [ -d \"'$path_to_check'\" ]; then '$COMMAND_SUDO' df \"'$path_to_check'\"; else exit 1; fi" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1'
Logger "cmd: $cmd" "DEBUG" Logger "cmd: $cmd" "DEBUG"
eval "$cmd" & eval "$cmd" &
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_DB_TASK $HARD_MAX_EXEC_TIME_DB_TASK ${FUNCNAME[0]} true $KEEP_LOGGING WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_DB_TASK $HARD_MAX_EXEC_TIME_DB_TASK ${FUNCNAME[0]} true $KEEP_LOGGING
@ -2150,6 +2350,9 @@ function GetDiskSpaceRemote {
else else
DISK_SPACE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $4}') DISK_SPACE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $4}')
DRIVE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $1}') DRIVE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $1}')
if [ $(IsInteger $DISK_SPACE) -eq 0 ]; then
DISK_SPACE="$(HumanToNumeric $DISK_SPACE)"
fi
fi fi
} }
@ -2951,8 +3154,11 @@ function Init {
fi fi
if [ "$SSH_RSA_PRIVATE_KEY" == "" ]; then if [ "$SSH_RSA_PRIVATE_KEY" == "" ]; then
if [ ! -f "$SSH_PASSWORD_FILE" ]; then
# Assume that there might exist a standard rsa key
SSH_RSA_PRIVATE_KEY=~/.ssh/id_rsa SSH_RSA_PRIVATE_KEY=~/.ssh/id_rsa
fi fi
fi
# remove everything before '@' # remove everything before '@'
hosturiandpath=${uri#*@} hosturiandpath=${uri#*@}
@ -3004,6 +3210,7 @@ function Main {
FILE_STORAGE="${FILE_STORAGE/#\~/$HOME}" FILE_STORAGE="${FILE_STORAGE/#\~/$HOME}"
SQL_STORAGE="${SQL_STORAGE/#\~/$HOME}" SQL_STORAGE="${SQL_STORAGE/#\~/$HOME}"
SSH_RSA_PRIVATE_KEY="${SSH_RSA_PRIVATE_KEY/#\~/$HOME}" SSH_RSA_PRIVATE_KEY="${SSH_RSA_PRIVATE_KEY/#\~/$HOME}"
SSH_PASSWORD_FILE="${SSH_PASSWORD_FILE/#\~/$HOME}"
ENCRYPT_PUBKEY="${ENCRYPT_PUBKEY/#\~/$HOME}" ENCRYPT_PUBKEY="${ENCRYPT_PUBKEY/#\~/$HOME}"
if [ "$CREATE_DIRS" != "no" ]; then if [ "$CREATE_DIRS" != "no" ]; then
@ -3158,8 +3365,16 @@ else
LOG_FILE="$LOGFILE" LOG_FILE="$LOGFILE"
fi fi
fi
if [ ! -w "$(dirname $LOG_FILE)" ]; then
echo "Cannot write to log [$(dirname $LOG_FILE)]."
else
Logger "Script begin, logging to [$LOG_FILE]." "DEBUG"
fi
if [ "$IS_STABLE" != "yes" ]; then if [ "$IS_STABLE" != "yes" ]; then
Logger "This is an unstable dev build. Please use with caution." "WARN" Logger "This is an unstable dev build [$PROGRAM_BUILD]. Please use with caution." "WARN"
fi fi
DATE=$(date) DATE=$(date)
@ -3170,10 +3385,10 @@ Logger "Backup instance [$INSTANCE_ID] launched as $LOCAL_USER@$LOCAL_HOST (PID
GetLocalOS GetLocalOS
InitLocalOSSettings InitLocalOSSettings
CheckEnvironment
CheckRunningInstances CheckRunningInstances
PreInit PreInit
Init Init
CheckEnvironment
PostInit PostInit
CheckCurrentConfig CheckCurrentConfig

View File

@ -1,7 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#TODO: missing files says Backup succeed #TODO: missing files says Backup succeed
#TODO: add new encryption variable checks, also upgrade script
#TODO: ListingDatabases fail succeed #TODO: ListingDatabases fail succeed
#TODO: Add .gpg extesion to RotateFiles ? #TODO: Add .gpg extesion to RotateFiles ?
@ -10,12 +9,12 @@ 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=2016090901 PROGRAM_BUILD=2016111002
IS_STABLE=no IS_STABLE=no
#### MINIMAL-FUNCTION-SET BEGIN #### #### MINIMAL-FUNCTION-SET BEGIN ####
## FUNC_BUILD=2016090701 ## FUNC_BUILD=2016111002
## BEGIN Generic bash functions 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: ## To use in a program, define the following variables:
@ -23,6 +22,8 @@ IS_STABLE=no
## INSTANCE_ID=program-instance-name ## INSTANCE_ID=program-instance-name
## _DEBUG=yes/no ## _DEBUG=yes/no
#TODO(high): Refactor GetRemoteOs into big oneliner for faster execution (if busybox else uname else uname -spio in one statement)
#TODO(high): Implement busybox support in SendEmail function
#TODO: Windows checks, check sendmail & mailsend #TODO: Windows checks, check sendmail & mailsend
if ! type "$BASH" > /dev/null; then if ! type "$BASH" > /dev/null; then
@ -94,7 +95,7 @@ fi
# Default alert attachment filename # Default alert attachment filename
ALERT_LOG_FILE="$RUN_DIR/$PROGRAM.last.log" ALERT_LOG_FILE="$RUN_DIR/$PROGRAM.$SCRIPT_PID.last.log"
# Set error exit code if a piped command fails # Set error exit code if a piped command fails
set -o pipefail set -o pipefail
@ -115,7 +116,7 @@ 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 == true ]; then if [ $_LOGGER_STDERR == true ] && [ "$evalue" != "" ]; then
cat <<< "$evalue" 1>&2 cat <<< "$evalue" 1>&2
elif [ "$_SILENT" == false ]; then elif [ "$_SILENT" == false ]; then
echo -e "$svalue" echo -e "$svalue"
@ -299,6 +300,30 @@ function SendAlert {
if [ "$mail_no_attachment" -eq 0 ]; then if [ "$mail_no_attachment" -eq 0 ]; then
attachment_command="-a $ALERT_LOG_FILE" attachment_command="-a $ALERT_LOG_FILE"
fi fi
if [ "$LOCAL_OS" == "BUSYBOX" ]; then
if type sendmail > /dev/null 2>&1; then
if [ "$ENCRYPTION" == "tls" ]; then
echo -e "Subject:$subject\r\n$body" | $(type -p sendmail) -f "$SENDER_MAIL" -H "exec openssl s_client -quiet -tls1_2 -starttls smtp -connect $SMTP_SERVER:$SMTP_PORT" -au"$SMTP_USER" -ap"$SMTP_PASSWORD" $DESTINATION_MAILS
elif [ "$ENCRYPTION" == "ssl" ]; then
echo -e "Subject:$subject\r\n$body" | $(type -p sendmail) -f "$SENDER_MAIL" -H "exec openssl s_client -quiet -connect $SMTP_SERVER:$SMTP_PORT" -au"$SMTP_USER" -ap"$SMTP_PASSWORD" $DESTINATION_MAILS
else
echo -e "Subject:$subject\r\n$body" | $(type -p sendmail) -f "$SENDER_MAIL" -S "$SMTP_SERVER:$SMTP_PORT" -au"$SMTP_USER" -ap"$SMTP_PASSWORD" $DESTINATION_MAILS
fi
if [ $? != 0 ]; then
Logger "Cannot send alert mail via $(type -p sendmail) !!!" "WARN"
# Don't bother try other mail systems with busybox
return 1
else
return 0
fi
else
Logger "Sendmail not present. Won't send any mail" "WARN"
return 1
fi
fi
if type mutt > /dev/null 2>&1 ; then if type mutt > /dev/null 2>&1 ; then
echo "$body" | $(type -p mutt) -x -s "$subject" $DESTINATION_MAILS $attachment_command echo "$body" | $(type -p mutt) -x -s "$subject" $DESTINATION_MAILS $attachment_command
if [ $? != 0 ]; then if [ $? != 0 ]; then
@ -398,7 +423,7 @@ function SendAlert {
# Delete tmp log file # Delete tmp log file
if [ -f "$ALERT_LOG_FILE" ]; then if [ -f "$ALERT_LOG_FILE" ]; then
rm "$ALERT_LOG_FILE" rm -f "$ALERT_LOG_FILE"
fi fi
} }
@ -407,21 +432,21 @@ function SendAlert {
# SendEmail "subject" "Body text" "receiver@example.com receiver2@otherdomain.com" "/path/to/attachment.file" # SendEmail "subject" "Body text" "receiver@example.com receiver2@otherdomain.com" "/path/to/attachment.file"
# Usage (Windows, make sure you have mailsend.exe in executable path, see http://github.com/muquit/mailsend) # Usage (Windows, make sure you have mailsend.exe in executable path, see http://github.com/muquit/mailsend)
# attachment is optional but must be in windows format like "c:\\some\path\\my.file", or "" # attachment is optional but must be in windows format like "c:\\some\path\\my.file", or ""
# smtp_server.domain.tld is mandatory, as is smtp_port (should be 25, 465 or 587) # smtp_server.domain.tld is mandatory, as is smtpPort (should be 25, 465 or 587)
# encryption can be set to tls, ssl or none # encryption can be set to tls, ssl or none
# smtp_user and smtp_password are optional # smtpUser and smtpPassword are optional
# SendEmail "subject" "Body text" "receiver@example.com receiver2@otherdomain.com" "/path/to/attachment.file" "sender_email@example.com" "smtp_server.domain.tld" "smtp_port" "encryption" "smtp_user" "smtp_password" # SendEmail "subject" "Body text" "receiver@example.com receiver2@otherdomain.com" "/path/to/attachment.file" "senderMail@example.com" "smtpServer.domain.tld" "smtpPort" "encryption" "smtpUser" "smtpPassword"
function SendEmail { function SendEmail {
local subject="${1}" local subject="${1}"
local message="${2}" local message="${2}"
local destination_mails="${3}" local destinationMails="${3}"
local attachment="${4}" local attachment="${4}"
local sender_email="${5}" local senderMail="${5}"
local smtp_server="${6}" local smtpServer="${6}"
local smtp_port="${7}" local smtpPort="${7}"
local encryption="${8}" local encryption="${8}"
local smtp_user="${9}" local smtpUser="${9}"
local smtp_password="${10}" local smtpPassword="${10}"
# CheckArguments will report a warning that can be ignored if used in Windows with paranoia debug enabled # CheckArguments will report a warning that can be ignored if used in Windows with paranoia debug enabled
@ -432,14 +457,37 @@ function SendEmail {
local auth_string= local auth_string=
if [ ! -f "$attachment" ]; then if [ ! -f "$attachment" ]; then
attachment_command="-a $ALERT_LOG_FILE" attachment_command="-a $attachment"
mail_no_attachment=1 mail_no_attachment=1
else else
mail_no_attachment=0 mail_no_attachment=0
fi fi
if [ "$LOCAL_OS" == "BUSYBOX" ]; then
if type sendmail > /dev/null 2>&1; then
if [ "$ENCRYPTION" == "tls" ]; then
echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) -f "$SenderMail" -H "exec openssl s_client -quiet -tls1_2 -starttls smtp -connect $smtpServer:$smtpPort" -au"$smtpUser" -ap"$smtpPassword" "$destinationMails"
elif [ "$ENCRYPTION" == "ssl" ]; then
echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) -f "$SenderMail" -H "exec openssl s_client -quiet -connect $smtpServer:$smtpPort" -au"$smtpUser" -ap"$smtpPassword" "$destinationMails"
else
echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) -f "$SenderMail" -S "$smtpServer:$SmtpPort" -au"$smtpUser" -ap"$smtpPassword" "$destinationMails"
fi
if [ $? != 0 ]; then
Logger "Cannot send alert mail via $(type -p sendmail) !!!" "WARN"
# Don't bother try other mail systems with busybox
return 1
else
return 0
fi
else
Logger "Sendmail not present. Won't send any mail" "WARN"
return 1
fi
fi
if type mutt > /dev/null 2>&1 ; then if type mutt > /dev/null 2>&1 ; then
echo "$message" | $(type -p mutt) -x -s "$subject" "$destination_mails" $attachment_command echo "$message" | $(type -p mutt) -x -s "$subject" "$destinationMails" $attachment_command
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send mail via $(type -p mutt) !!!" "WARN" Logger "Cannot send mail via $(type -p mutt) !!!" "WARN"
else else
@ -456,10 +504,10 @@ function SendEmail {
else else
attachment_command="" attachment_command=""
fi fi
echo "$message" | $(type -p mail) $attachment_command -s "$subject" "$destination_mails" echo "$message" | $(type -p mail) $attachment_command -s "$subject" "$destinationMails"
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send mail via $(type -p mail) with attachments !!!" "WARN" Logger "Cannot send mail via $(type -p mail) with attachments !!!" "WARN"
echo "$message" | $(type -p mail) -s "$subject" "$destination_mails" echo "$message" | $(type -p mail) -s "$subject" "$destinationMails"
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send mail via $(type -p mail) without attachments !!!" "WARN" Logger "Cannot send mail via $(type -p mail) without attachments !!!" "WARN"
else else
@ -473,7 +521,7 @@ function SendEmail {
fi fi
if type sendmail > /dev/null 2>&1 ; then if type sendmail > /dev/null 2>&1 ; then
echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) "$destination_mails" echo -e "Subject:$subject\r\n$message" | $(type -p sendmail) "$destinationMails"
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send mail via $(type -p sendmail) !!!" "WARN" Logger "Cannot send mail via $(type -p sendmail) !!!" "WARN"
else else
@ -484,17 +532,17 @@ function SendEmail {
# Windows specific # Windows specific
if type "mailsend.exe" > /dev/null 2>&1 ; then if type "mailsend.exe" > /dev/null 2>&1 ; then
if [ "$sender_email" == "" ]; then if [ "$senderMail" == "" ]; then
Logger "Missing sender email." "ERROR" Logger "Missing sender email." "ERROR"
return 1 return 1
fi fi
if [ "$smtp_server" == "" ]; then if [ "$smtpServer" == "" ]; then
Logger "Missing smtp port." "ERROR" Logger "Missing smtp port." "ERROR"
return 1 return 1
fi fi
if [ "$smtp_port" == "" ]; then if [ "$smtpPort" == "" ]; then
Logger "Missing smtp port, assuming 25." "WARN" Logger "Missing smtp port, assuming 25." "WARN"
smtp_port=25 smtpPort=25
fi fi
if [ "$encryption" != "tls" ] && [ "$encryption" != "ssl" ] && [ "$encryption" != "none" ]; then if [ "$encryption" != "tls" ] && [ "$encryption" != "ssl" ] && [ "$encryption" != "none" ]; then
Logger "Bogus smtp encryption, assuming none." "WARN" Logger "Bogus smtp encryption, assuming none." "WARN"
@ -504,10 +552,10 @@ function SendEmail {
elif [ "$encryption" == "ssl" ]:; then elif [ "$encryption" == "ssl" ]:; then
encryption_string=-ssl encryption_string=-ssl
fi fi
if [ "$smtp_user" != "" ] && [ "$smtp_password" != "" ]; then if [ "$smtpUser" != "" ] && [ "$smtpPassword" != "" ]; then
auth_string="-auth -user \"$smtp_user\" -pass \"$smtp_password\"" auth_string="-auth -user \"$smtpUser\" -pass \"$smtpPassword\""
fi fi
$(type mailsend.exe) -f "$sender_email" -t "$destination_mails" -sub "$subject" -M "$message" -attach "$attachment" -smtp "$smtp_server" -port "$smtp_port" $encryption_string $auth_string $(type mailsend.exe) -f "$senderMail" -t "$destinationMails" -sub "$subject" -M "$message" -attach "$attachment" -smtp "$smtpServer" -port "$smtpPort" $encryption_string $auth_string
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send mail via $(type mailsend.exe) !!!" "WARN" Logger "Cannot send mail via $(type mailsend.exe) !!!" "WARN"
else else
@ -535,6 +583,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 == false ]; 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
@ -544,6 +593,7 @@ function LoadConfigFile {
local configFile="${1}" local configFile="${1}"
if [ ! -f "$configFile" ]; then if [ ! -f "$configFile" ]; then
Logger "Cannot load configuration file [$configFile]. Cannot start." "CRITICAL" Logger "Cannot load configuration file [$configFile]. Cannot start." "CRITICAL"
exit 1 exit 1
@ -604,11 +654,11 @@ function joinString {
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 softMaxTime="${2}" # If program with pid $pid takes longer than $softMaxTime seconds, will log a warning, unless $softMaxTime 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 hardMaxTime="${3}" # If program with pid $pid takes longer than $hardMaxTime seconds, will stop execution, unless $hardMaxTime equals 0.
local caller_name="${4}" # Who called this function local callerName="${4}" # Who called this function
local counting="${5:-true}" # Count time since function has been launched if true, since script has been launched if false local counting="${5:-true}" # Count time since function has been launched if true, since script has been launched if false
local keep_logging="${6:-0}" # Log a standby message every X seconds. Set to zero to disable logging local keepLogging="${6:-0}" # Log a standby message every X seconds. Set to zero to disable logging
local soft_alert=false # 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
@ -643,8 +693,8 @@ function WaitForTaskCompletion {
exec_time=$SECONDS exec_time=$SECONDS
fi fi
if [ $keep_logging -ne 0 ]; then if [ $keepLogging -ne 0 ]; then
if [ $((($exec_time + 1) % $keep_logging)) -eq 0 ]; then if [ $((($exec_time + 1) % $keepLogging)) -eq 0 ]; then
if [ $log_ttime -ne $exec_time ]; then # Fix when sleep time lower than 1s if [ $log_ttime -ne $exec_time ]; then # Fix when sleep time lower than 1s
log_ttime=$exec_time log_ttime=$exec_time
Logger "Current tasks still running with pids [$(joinString , ${pidsArray[@]})]." "NOTICE" Logger "Current tasks still running with pids [$(joinString , ${pidsArray[@]})]." "NOTICE"
@ -652,15 +702,15 @@ function WaitForTaskCompletion {
fi fi
fi fi
if [ $exec_time -gt $soft_max_time ]; then if [ $exec_time -gt $softMaxTime ]; then
if [ $soft_alert == true ] && [ $soft_max_time -ne 0 ]; then if [ $soft_alert == true ] && [ $softMaxTime -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 [$callerName] with pids [$(joinString , ${pidsArray[@]})]." "WARN"
soft_alert=true soft_alert=true
SendAlert true SendAlert true
fi fi
if [ $exec_time -gt $hard_max_time ] && [ $hard_max_time -ne 0 ]; then if [ $exec_time -gt $hardMaxTime ] && [ $hardMaxTime -ne 0 ]; then
Logger "Max hard execution time exceeded for task [$caller_name] with pids [$(joinString , ${pidsArray[@]})]. Stopping task execution." "ERROR" Logger "Max hard execution time exceeded for task [$callerName] with pids [$(joinString , ${pidsArray[@]})]. Stopping task execution." "ERROR"
for pid in "${pidsArray[@]}"; do for pid in "${pidsArray[@]}"; do
KillChilds $pid true KillChilds $pid true
if [ $? == 0 ]; then if [ $? == 0 ]; then
@ -677,8 +727,10 @@ function WaitForTaskCompletion {
if [ $(IsInteger $pid) -eq 1 ]; then if [ $(IsInteger $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 & busybox.
pidState=$(ps -p$pid -o state= 2 > /dev/null) #TODO(high): propagate changes to ParallelExec
#pidState=$(ps -p$pid -o state= 2 > /dev/null)
pidState="$(eval $PROCESS_STATE_CMD)"
if [ "$pidState" != "D" ] && [ "$pidState" != "Z" ]; then if [ "$pidState" != "D" ] && [ "$pidState" != "Z" ]; then
newPidsArray+=($pid) newPidsArray+=($pid)
fi fi
@ -688,7 +740,7 @@ 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 [$retval]." "DEBUG" Logger "${FUNCNAME[0]} called by [$callerName] finished monitoring [$pid] with exitcode [$retval]." "DEBUG"
if [ "$WAIT_FOR_TASK_COMPLETION" == "" ]; then if [ "$WAIT_FOR_TASK_COMPLETION" == "" ]; then
WAIT_FOR_TASK_COMPLETION="$pid:$retval" WAIT_FOR_TASK_COMPLETION="$pid:$retval"
else else
@ -721,6 +773,11 @@ function ParallelExec {
local numberOfProcesses="${1}" # Number of simultaneous commands to run local numberOfProcesses="${1}" # Number of simultaneous commands to run
local commandsArg="${2}" # Semi-colon separated list of commands, or file containing one command per line local commandsArg="${2}" # Semi-colon separated list of commands, or file containing one command per line
local readFromFile="${3:-false}" # Is commandsArg a file or a string ? local readFromFile="${3:-false}" # Is commandsArg a file or a string ?
local softMaxTime="${4:-0}"
local hardMaxTime="${5:-0}"
local callerName="${6}" # Who called this function
local counting="${7:-true}" # Count time since function has been launched if true, since script has been launched if false
local keepLogging="${8:-0}" # Log a standby message every X seconds. Set to zero to disable logging
local commandCount local commandCount
@ -759,7 +816,7 @@ function ParallelExec {
command="${commandsArray[$counter]}" command="${commandsArray[$counter]}"
fi fi
Logger "Running command [$command]." "DEBUG" Logger "Running command [$command]." "DEBUG"
eval "$command" & eval "$command" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" 2>&1 &
pid=$! pid=$!
pidsArray+=($pid) pidsArray+=($pid)
commandsArrayPid[$pid]="$command" commandsArrayPid[$pid]="$command"
@ -772,7 +829,8 @@ function ParallelExec {
if [ $(IsInteger $pid) -eq 1 ]; then if [ $(IsInteger $pid) -eq 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 ? :)
if kill -0 $pid > /dev/null 2>&1; then if kill -0 $pid > /dev/null 2>&1; then
pidState=$(ps -p$pid -o state= 2 > /dev/null) #pidState=$(ps -p$pid -o state= 2 > /dev/null)
pidState="$(eval $PROCESS_STATE_CMD)"
if [ "$pidState" != "D" ] && [ "$pidState" != "Z" ]; then if [ "$pidState" != "D" ] && [ "$pidState" != "Z" ]; then
newPidsArray+=($pid) newPidsArray+=($pid)
fi fi
@ -806,8 +864,6 @@ function CleanUp {
fi fi
} }
#### MINIMAL-FUNCTION-SET END ####
# obsolete, use StripQuotes # obsolete, use StripQuotes
function SedStripQuotes { function SedStripQuotes {
echo $(echo $1 | sed "s/^\([\"']\)\(.*\)\1\$/\2/g") echo $(echo $1 | sed "s/^\([\"']\)\(.*\)\1\$/\2/g")
@ -816,6 +872,7 @@ function SedStripQuotes {
# Usage: var=$(StripSingleQuotes "$var") # Usage: var=$(StripSingleQuotes "$var")
function StripSingleQuotes { function StripSingleQuotes {
local string="${1}" local string="${1}"
string="${string/#\'/}" # Remove singlequote if it begins string string="${string/#\'/}" # Remove singlequote if it begins string
string="${string/%\'/}" # Remove singlequote if it ends string string="${string/%\'/}" # Remove singlequote if it ends string
echo "$string" echo "$string"
@ -824,6 +881,7 @@ function StripSingleQuotes {
# Usage: var=$(StripDoubleQuotes "$var") # Usage: var=$(StripDoubleQuotes "$var")
function StripDoubleQuotes { function StripDoubleQuotes {
local string="${1}" local string="${1}"
string="${string/#\"/}" string="${string/#\"/}"
string="${string/%\"/}" string="${string/%\"/}"
echo "$string" echo "$string"
@ -831,12 +889,14 @@ function StripDoubleQuotes {
function StripQuotes { function StripQuotes {
local string="${1}" local string="${1}"
echo "$(StripSingleQuotes $(StripDoubleQuotes $string))" echo "$(StripSingleQuotes $(StripDoubleQuotes $string))"
} }
# Usage var=$(EscapeSpaces "$var") or var="$(EscapeSpaces "$var")" # Usage var=$(EscapeSpaces "$var") or var="$(EscapeSpaces "$var")"
function EscapeSpaces { function EscapeSpaces {
local string="${1}" # String on which spaces will be escaped local string="${1}" # String on which spaces will be escaped
echo "${string// /\\ }" echo "${string// /\\ }"
} }
@ -844,20 +904,22 @@ function IsNumericExpand {
eval "local value=\"${1}\"" # Needed eval so variable variables can be processed eval "local value=\"${1}\"" # Needed eval so variable variables can be processed
local re="^-?[0-9]+([.][0-9]+)?$" local re="^-?[0-9]+([.][0-9]+)?$"
if [[ $value =~ $re ]]; then if [[ $value =~ $re ]]; then
echo 1 echo 1 && return 1
else else
echo 0 echo 0 && return 0
fi fi
} }
# Usage [ $(IsNumeric $var) -eq 1 ]
function IsNumeric { function IsNumeric {
local value="${1}" local value="${1}"
if [[ $value =~ ^[0-9]+([.][0-9]+)?$ ]]; then if [[ $value =~ ^[0-9]+([.][0-9]+)?$ ]]; then
echo 1 echo 1 && return 1
else else
echo 0 echo 0 && return 0
fi fi
} }
@ -865,12 +927,43 @@ function IsInteger {
local value="${1}" local value="${1}"
if [[ $value =~ ^[0-9]+$ ]]; then if [[ $value =~ ^[0-9]+$ ]]; then
echo 1 echo 1 && return 1
else else
echo 0 echo 0 && return 0
fi fi
} }
# Converts human readable sizes into integer kilobyte sizes
# Usage numericSize="$(HumanToNumeric $humanSize)"
function HumanToNumeric {
local value="${1}"
local notation
local suffix
local suffixPresent
local multiplier
notation=(K M G T P E)
for suffix in "${notation[@]}"; do
multiplier=$((multiplier+1))
if [[ "$value" == *"$suffix"* ]]; then
suffixPresent=$suffix
break;
fi
done
if [ "$suffixPresent" != "" ]; then
value=${value%$suffix*}
value=${value%.*}
# /1024 since we convert to kilobytes instead of bytes
value=$((value*(1024**multiplier/1024)))
else
value=${value%.*}
fi
echo $value
}
## from https://gist.github.com/cdown/1163649 ## from https://gist.github.com/cdown/1163649
function urlEncode { function urlEncode {
local length="${#1}" local length="${#1}"
@ -890,24 +983,45 @@ function urlEncode {
} }
function urlDecode { function urlDecode {
local url_encoded="${1//+/ }" local urlEncoded="${1//+/ }"
printf '%b' "${url_encoded//%/\\x}" printf '%b' "${urlEncoded//%/\\x}"
}
## Modified version of http://stackoverflow.com/a/8574392
## Usage: arrayContains "needle" "${haystack[@]}"
arrayContains () {
local e
if [ "$2" == "" ]; then
echo 0 && return 0
fi
for e in "${@:2}"; do
[[ "$e" == "$1" ]] && echo 1 && return 1
done
echo 0 && return 0
} }
function GetLocalOS { function GetLocalOS {
local local_os_var= local localOsVar
local_os_var="$(uname -spio 2>&1)" # There's no good way to tell if currently running in BusyBox shell. Using sluggish way.
ls --help 2>&1 | grep -i BusyBox > /dev/null
if [ $? == 0 ]; then
localOsVar="BusyBox"
else
localOsVar="$(uname -spio 2>&1)"
if [ $? != 0 ]; then if [ $? != 0 ]; then
local_os_var="$(uname -v 2>&1)" localOsVar="$(uname -v 2>&1)"
if [ $? != 0 ]; then if [ $? != 0 ]; then
local_os_var="$(uname)" localOsVar="$(uname)"
fi
fi fi
fi fi
case $local_os_var in case $localOsVar in
*"Linux"*) *"Linux"*)
LOCAL_OS="Linux" LOCAL_OS="Linux"
;; ;;
@ -920,27 +1034,41 @@ function GetLocalOS {
*"Darwin"*) *"Darwin"*)
LOCAL_OS="MacOSX" LOCAL_OS="MacOSX"
;; ;;
*"BusyBox"*)
LOCAL_OS="BUSYBOX"
;;
*) *)
if [ "$IGNORE_OS_TYPE" == "yes" ]; then #DOC: Undocumented option if [ "$IGNORE_OS_TYPE" == "yes" ]; then #TODO(doc): Undocumented option
Logger "Running on unknown local OS [$local_os_var]." "WARN" Logger "Running on unknown local OS [$localOsVar]." "WARN"
return return
fi fi
Logger "Running on >> $local_os_var << not supported. Please report to the author." "ERROR" Logger "Running on >> $localOsVar << not supported. Please report to the author." "ERROR"
exit 1 exit 1
;; ;;
esac esac
Logger "Local OS: [$local_os_var]." "DEBUG" Logger "Local OS: [$localOsVar]." "DEBUG"
} }
#### MINIMAL-FUNCTION-SET END ####
function GetRemoteOS { function GetRemoteOS {
local cmd= local retval
local remote_os_var= local cmd
local remoteOsVar
if [ "$REMOTE_OPERATION" == "yes" ]; then if [ "$REMOTE_OPERATION" == "yes" ]; then
CheckConnectivity3rdPartyHosts CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost CheckConnectivityRemoteHost
cmd=$SSH_CMD' "ls --help 2>&1 | grep -i BusyBox > /dev/null"'
Logger "cmd: $cmd" "DEBUG"
eval "$cmd" &
WaitForTaskCompletion $! 120 240 ${FUNCNAME[0]}"-0" true $KEEP_LOGGING
retval=$?
if [ $retval == 0 ]; then
remoteOsVar="BusyBox"
else
cmd=$SSH_CMD' "uname -spio" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1' cmd=$SSH_CMD' "uname -spio" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1'
Logger "cmd: $cmd" "DEBUG" Logger "cmd: $cmd" "DEBUG"
eval "$cmd" & eval "$cmd" &
@ -963,10 +1091,10 @@ function GetRemoteOS {
fi fi
fi fi
fi fi
remoteOsVar=$(cat "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID")
fi
remote_os_var=$(cat "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID") case $remoteOsVar in
case $remote_os_var in
*"Linux"*) *"Linux"*)
REMOTE_OS="Linux" REMOTE_OS="Linux"
;; ;;
@ -979,27 +1107,30 @@ function GetRemoteOS {
*"Darwin"*) *"Darwin"*)
REMOTE_OS="MacOSX" REMOTE_OS="MacOSX"
;; ;;
*"BusyBox"*)
REMOTE_OS="BUSYBOX"
;;
*"ssh"*|*"SSH"*) *"ssh"*|*"SSH"*)
Logger "Cannot connect to remote system." "CRITICAL" Logger "Cannot connect to remote system." "CRITICAL"
exit 1 exit 1
;; ;;
*) *)
if [ "$IGNORE_OS_TYPE" == "yes" ]; then #DOC: Undocumented option if [ "$IGNORE_OS_TYPE" == "yes" ]; then #DOC: Undocumented option
Logger "Running on unknown remote OS [$remote_os_var]." "WARN" Logger "Running on unknown remote OS [$remoteOsVar]." "WARN"
return return
fi fi
Logger "Running on remote OS failed. Please report to the author if the OS is not supported." "CRITICAL" 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" Logger "Remote OS said:\n$remoteOsVar" "CRITICAL"
exit 1 exit 1
esac esac
Logger "Remote OS: [$remote_os_var]." "DEBUG" Logger "Remote OS: [$remoteOsVar]." "DEBUG"
fi fi
} }
function RunLocalCommand { 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 hardMaxTime="${2}" # Max time to wait for command to compleet
if [ $_DRYRUN == true ]; then if [ $_DRYRUN == true ]; then
Logger "Dryrun: Local command [$command] not run." "NOTICE" Logger "Dryrun: Local command [$command] not run." "NOTICE"
@ -1008,7 +1139,7 @@ function RunLocalCommand {
Logger "Running command [$command] on local host." "NOTICE" Logger "Running command [$command] on local host." "NOTICE"
eval "$command" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" 2>&1 & eval "$command" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" 2>&1 &
WaitForTaskCompletion $! 0 $hard_max_time ${FUNCNAME[0]} true $KEEP_LOGGING WaitForTaskCompletion $! 0 $hardMaxTime ${FUNCNAME[0]} true $KEEP_LOGGING
retval=$? retval=$?
if [ $retval -eq 0 ]; then if [ $retval -eq 0 ]; then
Logger "Command succeded." "NOTICE" Logger "Command succeded." "NOTICE"
@ -1029,7 +1160,7 @@ function RunLocalCommand {
## Runs remote command $1 and waits for completition in $2 seconds ## Runs remote command $1 and waits for completition in $2 seconds
function RunRemoteCommand { function RunRemoteCommand {
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 hardMaxTime="${2}" # Max time to wait for command to compleet
CheckConnectivity3rdPartyHosts CheckConnectivity3rdPartyHosts
CheckConnectivityRemoteHost CheckConnectivityRemoteHost
@ -1042,7 +1173,7 @@ function RunRemoteCommand {
cmd=$SSH_CMD' "$command" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1' cmd=$SSH_CMD' "$command" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1'
Logger "cmd: $cmd" "DEBUG" Logger "cmd: $cmd" "DEBUG"
eval "$cmd" & eval "$cmd" &
WaitForTaskCompletion $! 0 $hard_max_time ${FUNCNAME[0]} true $KEEP_LOGGING WaitForTaskCompletion $! 0 $hardMaxTime ${FUNCNAME[0]} true $KEEP_LOGGING
retval=$? retval=$?
if [ $retval -eq 0 ]; then if [ $retval -eq 0 ]; then
Logger "Command succeded." "NOTICE" Logger "Command succeded." "NOTICE"
@ -1063,7 +1194,7 @@ function RunRemoteCommand {
function RunBeforeHook { function RunBeforeHook {
local pids= local pids
if [ "$LOCAL_RUN_BEFORE_CMD" != "" ]; then if [ "$LOCAL_RUN_BEFORE_CMD" != "" ]; then
RunLocalCommand "$LOCAL_RUN_BEFORE_CMD" $MAX_EXEC_TIME_PER_CMD_BEFORE & RunLocalCommand "$LOCAL_RUN_BEFORE_CMD" $MAX_EXEC_TIME_PER_CMD_BEFORE &
@ -1108,7 +1239,7 @@ function CheckConnectivityRemoteHost {
WaitForTaskCompletion $! 60 180 ${FUNCNAME[0]} true $KEEP_LOGGING WaitForTaskCompletion $! 60 180 ${FUNCNAME[0]} true $KEEP_LOGGING
retval=$? retval=$?
if [ $retval != 0 ]; then if [ $retval != 0 ]; then
Logger "Cannot ping [$REMOTE_HOST]. Return code [$retval]." "ERROR" Logger "Cannot ping [$REMOTE_HOST]. Return code [$retval]." "WARN"
return $retval return $retval
fi fi
fi fi
@ -1117,13 +1248,13 @@ function CheckConnectivityRemoteHost {
function CheckConnectivity3rdPartyHosts { function CheckConnectivity3rdPartyHosts {
local remote_3rd_party_success local remote3rdPartySuccess
local retval local retval
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=false remote3rdPartySuccess=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" &
@ -1132,12 +1263,12 @@ function CheckConnectivity3rdPartyHosts {
if [ $retval != 0 ]; then if [ $retval != 0 ]; then
Logger "Cannot ping 3rd party host [$i]. Return code [$retval]." "NOTICE" Logger "Cannot ping 3rd party host [$i]. Return code [$retval]." "NOTICE"
else else
remote_3rd_party_success=true remote3rdPartySuccess=true
fi fi
done done
if [ $remote_3rd_party_success == false ]; then if [ $remote3rdPartySuccess == 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 ?" "WARN"
return 1 return 1
else else
return 0 return 0
@ -1150,7 +1281,7 @@ function CheckConnectivity3rdPartyHosts {
#__END_WITH_PARANOIA_DEBUG #__END_WITH_PARANOIA_DEBUG
function RsyncPatternsAdd { function RsyncPatternsAdd {
local pattern_type="${1}" # exclude or include local patternType="${1}" # exclude or include
local pattern="${2}" local pattern="${2}"
local rest local rest
@ -1161,7 +1292,7 @@ function RsyncPatternsAdd {
while [ -n "$rest" ] while [ -n "$rest" ]
do do
# Take the string until first occurence until $PATH_SEPARATOR_CHAR # Take the string until first occurence until $PATH_SEPARATOR_CHAR
str=${rest%%;*} str=${rest%%;*} #TODO: replace ; with $PATH_SEPARATOR_CHAR
# Handle the last case # Handle the last case
if [ "$rest" = "${rest/$PATH_SEPARATOR_CHAR/}" ]; then if [ "$rest" = "${rest/$PATH_SEPARATOR_CHAR/}" ]; then
rest= rest=
@ -1170,25 +1301,25 @@ function RsyncPatternsAdd {
rest=${rest#*$PATH_SEPARATOR_CHAR} rest=${rest#*$PATH_SEPARATOR_CHAR}
fi fi
if [ "$RSYNC_PATTERNS" == "" ]; then if [ "$RSYNC_PATTERNS" == "" ]; then
RSYNC_PATTERNS="--"$pattern_type"=\"$str\"" RSYNC_PATTERNS="--"$patternType"=\"$str\""
else else
RSYNC_PATTERNS="$RSYNC_PATTERNS --"$pattern_type"=\"$str\"" RSYNC_PATTERNS="$RSYNC_PATTERNS --"$patternType"=\"$str\""
fi fi
done done
set +f set +f
} }
function RsyncPatternsFromAdd { function RsyncPatternsFromAdd {
local pattern_type="${1}" local patternType="${1}"
local pattern_from="${2}" local patternFrom="${2}"
## Check if the exclude list has a full path, and if not, add the config file path if there is one ## 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 if [ "$(basename $patternFrom)" == "$patternFrom" ]; then
pattern_from="$(dirname $CONFIG_FILE)/$pattern_from" patternFrom="$(dirname $CONFIG_FILE)/$patternFrom"
fi fi
if [ -e "$pattern_from" ]; then if [ -e "$patternFrom" ]; then
RSYNC_PATTERNS="$RSYNC_PATTERNS --"$pattern_type"-from=\"$pattern_from\"" RSYNC_PATTERNS="$RSYNC_PATTERNS --"$patternType"-from=\"$patternFrom\""
fi fi
} }
@ -1207,7 +1338,8 @@ function RsyncPatterns {
if [ "$RSYNC_INCLUDE_FROM" != "" ]; then if [ "$RSYNC_INCLUDE_FROM" != "" ]; then
RsyncPatternsFromAdd "include" "$RSYNC_INCLUDE_FROM" RsyncPatternsFromAdd "include" "$RSYNC_INCLUDE_FROM"
fi fi
elif [ "$RSYNC_PATTERN_FIRST" == "include" ]; then # Use default include first for quicksync runs
elif [ "$RSYNC_PATTERN_FIRST" == "include" ] || [ "$_QUICK_SYNC" == "2" ]; then
if [ "$RSYNC_INCLUDE_PATTERN" != "" ]; then if [ "$RSYNC_INCLUDE_PATTERN" != "" ]; then
RsyncPatternsAdd "include" "$RSYNC_INCLUDE_PATTERN" RsyncPatternsAdd "include" "$RSYNC_INCLUDE_PATTERN"
fi fi
@ -1227,6 +1359,8 @@ function RsyncPatterns {
function PreInit { function PreInit {
local compressionString
## SSH compression ## SSH compression
if [ "$SSH_COMPRESSION" != "no" ]; then if [ "$SSH_COMPRESSION" != "no" ]; then
SSH_COMP=-C SSH_COMP=-C
@ -1317,24 +1451,42 @@ function PreInit {
fi fi
## Set compression executable and extension ## Set compression executable and extension
if [ "$(IsInteger $COMPRESSION_LEVEL)" -eq 0 ]; then
COMPRESSION_LEVEL=3 COMPRESSION_LEVEL=3
fi
## Busybox fix (Termux xz command doesn't support compression at all)
if [ "$LOCAL_OS" == "BUSYBOX" ] || [ "$REMOTE_OS" == "BUSYBOX" ]; then
compressionString=""
if type gzip > /dev/null 2>&1
then
COMPRESSION_PROGRAM="| gzip -c$compressionString"
COMPRESSION_EXTENSION=.gz
# obackup specific
else
COMPRESSION_PROGRAM=
COMPRESSION_EXTENSION=
fi
else
compressionString=" -$COMPRESSION_LEVEL"
if type xz > /dev/null 2>&1 if type xz > /dev/null 2>&1
then then
COMPRESSION_PROGRAM="| xz -$COMPRESSION_LEVEL" COMPRESSION_PROGRAM="| xz -c$compressionString"
COMPRESSION_EXTENSION=.xz COMPRESSION_EXTENSION=.xz
elif type lzma > /dev/null 2>&1 elif type lzma > /dev/null 2>&1
then then
COMPRESSION_PROGRAM="| lzma -$COMPRESSION_LEVEL" COMPRESSION_PROGRAM="| lzma -c$compressionString"
COMPRESSION_EXTENSION=.lzma COMPRESSION_EXTENSION=.lzma
elif type pigz > /dev/null 2>&1 elif type pigz > /dev/null 2>&1
then then
COMPRESSION_PROGRAM="| pigz -$COMPRESSION_LEVEL" COMPRESSION_PROGRAM="| pigz -c$compressionString"
COMPRESSION_EXTENSION=.gz COMPRESSION_EXTENSION=.gz
# obackup specific # obackup specific
COMPRESSION_OPTIONS=--rsyncable COMPRESSION_OPTIONS=--rsyncable
elif type gzip > /dev/null 2>&1 elif type gzip > /dev/null 2>&1
then then
COMPRESSION_PROGRAM="| gzip -$COMPRESSION_LEVEL" COMPRESSION_PROGRAM="| gzip -c$compressionString"
COMPRESSION_EXTENSION=.gz COMPRESSION_EXTENSION=.gz
# obackup specific # obackup specific
COMPRESSION_OPTIONS=--rsyncable COMPRESSION_OPTIONS=--rsyncable
@ -1342,15 +1494,27 @@ function PreInit {
COMPRESSION_PROGRAM= COMPRESSION_PROGRAM=
COMPRESSION_EXTENSION= COMPRESSION_EXTENSION=
fi fi
fi
ALERT_LOG_FILE="$ALERT_LOG_FILE$COMPRESSION_EXTENSION" ALERT_LOG_FILE="$ALERT_LOG_FILE$COMPRESSION_EXTENSION"
} }
function PostInit { function PostInit {
# Define remote commands # Define remote commands
if [ -f "$SSH_RSA_PRIVATE_KEY" ]; then
SSH_CMD="$(type -p ssh) $SSH_COMP -i $SSH_RSA_PRIVATE_KEY $SSH_OPTS $REMOTE_USER@$REMOTE_HOST -p $REMOTE_PORT" SSH_CMD="$(type -p ssh) $SSH_COMP -i $SSH_RSA_PRIVATE_KEY $SSH_OPTS $REMOTE_USER@$REMOTE_HOST -p $REMOTE_PORT"
SCP_CMD="$(type -p scp) $SSH_COMP -i $SSH_RSA_PRIVATE_KEY -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 $SSH_OPTS -p $REMOTE_PORT" RSYNC_SSH_CMD="$(type -p ssh) $SSH_COMP -i $SSH_RSA_PRIVATE_KEY $SSH_OPTS -p $REMOTE_PORT"
elif [ -f "$SSH_PASSWORD_FILE" ]; then
SSH_CMD="$(type -p sshpass) -f $SSH_PASSWORD_FILE $(type -p ssh) $SSH_COMP $SSH_OPTS $REMOTE_USER@$REMOTE_HOST -p $REMOTE_PORT"
SCP_CMD="$(type -p sshpass) -f $SSH_PASSWORD_FILE $(type -p scp) $SSH_COMP -P $REMOTE_PORT"
RSYNC_SSH_CMD="$(type -p sshpass) -f $SSH_PASSWORD_FILE $(type -p ssh) $SSH_COMP $SSH_OPTS -p $REMOTE_PORT"
else
SSH_PASSWORD=""
SSH_CMD=""
SCP_CMD=""
RSYNC_SSH_CMD=""
fi
} }
function InitLocalOSSettings { function InitLocalOSSettings {
@ -1367,12 +1531,20 @@ function InitLocalOSSettings {
PING_CMD="ping -c 2 -i .2" PING_CMD="ping -c 2 -i .2"
fi fi
if [ "$LOCAL_OS" == "BUSYBOX" ]; then
PROCESS_STATE_CMD="echo none"
else
PROCESS_STATE_CMD='ps -p$pid -o state= 2 > /dev/null'
fi
## Stat command has different syntax on Linux and FreeBSD/MacOSX ## Stat command has different syntax on Linux and FreeBSD/MacOSX
if [ "$LOCAL_OS" == "MacOSX" ] || [ "$LOCAL_OS" == "BSD" ]; then if [ "$LOCAL_OS" == "MacOSX" ] || [ "$LOCAL_OS" == "BSD" ]; then
# Tested on BSD and Mac
STAT_CMD="stat -f \"%Sm\"" STAT_CMD="stat -f \"%Sm\""
STAT_CTIME_MTIME_CMD="stat -f %N;%c;%m" STAT_CTIME_MTIME_CMD="stat -f %N;%c;%m"
else else
STAT_CMD="stat --format %y" # Tested on GNU stat and busybox
STAT_CMD="stat -c %y"
STAT_CTIME_MTIME_CMD="stat -c %n;%Z;%Y" STAT_CTIME_MTIME_CMD="stat -c %n;%Z;%Y"
fi fi
} }
@ -1408,6 +1580,19 @@ function PrintIFS {
printf "IFS is: %q" "$IFS" printf "IFS is: %q" "$IFS"
} }
# Process debugging
# Recursive function to get all parents from a pid
function ParentPid {
local pid="${1}" # Pid to analyse
local parent
parent=$(ps -p $pid -o ppid=)
echo "$pid is a child of $parent"
if [ $parent -gt 0 ]; then
ParentPid $parent
fi
}
## END Generic functions ## END Generic functions
_LOGGER_PREFIX="time" _LOGGER_PREFIX="time"
@ -1489,6 +1674,11 @@ function CheckEnvironment {
CAN_BACKUP_SQL=false CAN_BACKUP_SQL=false
fi fi
fi fi
if [ "$SSH_PASSWORD_FILE" != "" ] && ! type sshpass > /dev/null 2>&1 ; then
Logger "sshpass not present. Cannot use password authentication." "CRITICAL"
exit 1
fi
fi fi
if [ "$FILE_BACKUP" != "no" ]; then if [ "$FILE_BACKUP" != "no" ]; then
@ -1527,7 +1717,7 @@ function CheckCurrentConfig {
# Check all variables that should contain "yes" or "no" # 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 SSH_COMPRESSION SSH_IGNORE_KNOWN_HOSTS REMOTE_HOST_PING SUDO_EXEC DATABASES_ALL PRESERVE_PERMISSIONS PRESERVE_OWNER PRESERVE_GROUP PRESERVE_EXECUTABILITY 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 RUN_AFTER_CMD_ON_ERROR) declare -a yes_no_vars=(SQL_BACKUP FILE_BACKUP ENCRYPTION CREATE_DIRS KEEP_ABSOLUTE_PATHS GET_BACKUP_SIZE SSH_COMPRESSION SSH_IGNORE_KNOWN_HOSTS REMOTE_HOST_PING SUDO_EXEC DATABASES_ALL PRESERVE_PERMISSIONS PRESERVE_OWNER PRESERVE_GROUP PRESERVE_EXECUTABILITY 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 RUN_AFTER_CMD_ON_ERROR)
for i in "${yes_no_vars[@]}"; do for i in "${yes_no_vars[@]}"; do
test="if [ \"\$$i\" != \"yes\" ] && [ \"\$$i\" != \"no\" ]; then Logger \"Bogus $i value defined in config file. Correct your config file or update it with the update script if using and old version.\" \"CRITICAL\"; exit 1; fi" test="if [ \"\$$i\" != \"yes\" ] && [ \"\$$i\" != \"no\" ]; then Logger \"Bogus $i value [$$i] defined in config file. Correct your config file or update it with the update script if using and old version.\" \"CRITICAL\"; exit 1; fi"
eval "$test" eval "$test"
done done
@ -1539,7 +1729,7 @@ function CheckCurrentConfig {
# Check all variables that should contain a numerical value >= 0 # Check all variables that should contain a numerical value >= 0
declare -a num_vars=(BACKUP_SIZE_MINIMUM 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 BANDWIDTH SOFT_MAX_EXEC_TIME_TOTAL HARD_MAX_EXEC_TIME_TOTAL ROTATE_SQL_COPIES ROTATE_FILE_COPIES KEEP_LOGGING MAX_EXEC_TIME_PER_CMD_BEFORE MAX_EXEC_TIME_PER_CMD_AFTER) declare -a num_vars=(BACKUP_SIZE_MINIMUM 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 BANDWIDTH SOFT_MAX_EXEC_TIME_TOTAL HARD_MAX_EXEC_TIME_TOTAL ROTATE_SQL_COPIES ROTATE_FILE_COPIES KEEP_LOGGING MAX_EXEC_TIME_PER_CMD_BEFORE MAX_EXEC_TIME_PER_CMD_AFTER)
for i in "${num_vars[@]}"; do for i in "${num_vars[@]}"; do
test="if [ $(IsNumericExpand \"\$$i\") -eq 0 ]; then Logger \"Bogus $i value defined in config file. Correct your config file or update it with the update script if using and old version.\" \"CRITICAL\"; exit 1; fi" test="if [ $(IsNumericExpand \"\$$i\") -eq 0 ]; then Logger \"Bogus $i value [$$i] defined in config file. Correct your config file or update it with the update script if using and old version.\" \"CRITICAL\"; exit 1; fi"
eval "$test" eval "$test"
done done
@ -1583,7 +1773,10 @@ function CheckCurrentConfig {
fi fi
fi fi
if [ "$REMOTE_OPERATION" == "yes" ] && ([ ! -f "$SSH_RSA_PRIVATE_KEY" ] && [ ! -f "$SSH_PASSWORD_FILE" ]); then
Logger "Cannot find rsa private key [$SSH_RSA_PRIVATE_KEY] nor password file [$SSH_PASSWORD_FILE]. No authentication method provided." "CRITICAL"
exit 1
fi
} }
function CheckRunningInstances { function CheckRunningInstances {
@ -1778,9 +1971,7 @@ function ListRecursiveBackupDirectories {
local output_file local output_file
local file_exclude local file_exclude
local excluded local excluded
local fileArray local fileArray
Logger "Listing directories to backup." "NOTICE" Logger "Listing directories to backup." "NOTICE"
@ -1870,6 +2061,9 @@ function _GetDirectoriesSizeLocal {
if [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then if [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then
TOTAL_FILES_SIZE="$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" TOTAL_FILES_SIZE="$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)"
if [ $(IsInteger $TOTAL_FILES_SIZE) -eq 0 ]; then
TOTAL_FILES_SIZE="$(HumanToNumeric $TOTAL_FILES_SIZE)"
fi
else else
TOTAL_FILES_SIZE=-1 TOTAL_FILES_SIZE=-1
fi fi
@ -1899,6 +2093,9 @@ function _GetDirectoriesSizeRemote {
fi fi
if [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then if [ -s "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" ]; then
TOTAL_FILES_SIZE="$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)" TOTAL_FILES_SIZE="$(cat $RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID)"
if [ $(IsInteger $TOTAL_FILES_SIZE) -eq 0 ]; then
TOTAL_FILES_SIZE="$(HumanToNumeric $TOTAL_FILES_SIZE)"
fi
else else
TOTAL_FILES_SIZE=-1 TOTAL_FILES_SIZE=-1
fi fi
@ -2004,7 +2201,7 @@ function GetDiskSpaceLocal {
if [ -d "$path_to_check" ]; then if [ -d "$path_to_check" ]; then
# Not elegant solution to make df silent on errors # 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 # No sudo on local commands, assuming you should have all the necesarry rights to check backup directories sizes
df -P "$path_to_check" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" 2>&1 df "$path_to_check" > "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" 2>&1
if [ $? != 0 ]; then if [ $? != 0 ]; then
DISK_SPACE=0 DISK_SPACE=0
Logger "Cannot get disk space in [$path_to_check] on local system." "ERROR" Logger "Cannot get disk space in [$path_to_check] on local system." "ERROR"
@ -2012,6 +2209,9 @@ function GetDiskSpaceLocal {
else else
DISK_SPACE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $4}') DISK_SPACE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $4}')
DRIVE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $1}') DRIVE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $1}')
if [ $(IsInteger $DISK_SPACE) -eq 0 ]; then
DISK_SPACE="$(HumanToNumeric $DISK_SPACE)"
fi
fi fi
else else
Logger "Storage path [$path_to_check] does not exist." "CRITICAL" Logger "Storage path [$path_to_check] does not exist." "CRITICAL"
@ -2025,7 +2225,7 @@ function GetDiskSpaceRemote {
local cmd 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' cmd=$SSH_CMD' "if [ -d \"'$path_to_check'\" ]; then '$COMMAND_SUDO' df \"'$path_to_check'\"; else exit 1; fi" > "'$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID'" 2>&1'
Logger "cmd: $cmd" "DEBUG" Logger "cmd: $cmd" "DEBUG"
eval "$cmd" & eval "$cmd" &
WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_DB_TASK $HARD_MAX_EXEC_TIME_DB_TASK ${FUNCNAME[0]} true $KEEP_LOGGING WaitForTaskCompletion $! $SOFT_MAX_EXEC_TIME_DB_TASK $HARD_MAX_EXEC_TIME_DB_TASK ${FUNCNAME[0]} true $KEEP_LOGGING
@ -2037,6 +2237,9 @@ function GetDiskSpaceRemote {
else else
DISK_SPACE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $4}') DISK_SPACE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $4}')
DRIVE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $1}') DRIVE=$(tail -1 "$RUN_DIR/$PROGRAM.${FUNCNAME[0]}.$SCRIPT_PID" | awk '{print $1}')
if [ $(IsInteger $DISK_SPACE) -eq 0 ]; then
DISK_SPACE="$(HumanToNumeric $DISK_SPACE)"
fi
fi fi
} }
@ -2818,8 +3021,11 @@ function Init {
fi fi
if [ "$SSH_RSA_PRIVATE_KEY" == "" ]; then if [ "$SSH_RSA_PRIVATE_KEY" == "" ]; then
if [ ! -f "$SSH_PASSWORD_FILE" ]; then
# Assume that there might exist a standard rsa key
SSH_RSA_PRIVATE_KEY=~/.ssh/id_rsa SSH_RSA_PRIVATE_KEY=~/.ssh/id_rsa
fi fi
fi
# remove everything before '@' # remove everything before '@'
hosturiandpath=${uri#*@} hosturiandpath=${uri#*@}
@ -2870,6 +3076,7 @@ function Main {
FILE_STORAGE="${FILE_STORAGE/#\~/$HOME}" FILE_STORAGE="${FILE_STORAGE/#\~/$HOME}"
SQL_STORAGE="${SQL_STORAGE/#\~/$HOME}" SQL_STORAGE="${SQL_STORAGE/#\~/$HOME}"
SSH_RSA_PRIVATE_KEY="${SSH_RSA_PRIVATE_KEY/#\~/$HOME}" SSH_RSA_PRIVATE_KEY="${SSH_RSA_PRIVATE_KEY/#\~/$HOME}"
SSH_PASSWORD_FILE="${SSH_PASSWORD_FILE/#\~/$HOME}"
ENCRYPT_PUBKEY="${ENCRYPT_PUBKEY/#\~/$HOME}" ENCRYPT_PUBKEY="${ENCRYPT_PUBKEY/#\~/$HOME}"
if [ "$CREATE_DIRS" != "no" ]; then if [ "$CREATE_DIRS" != "no" ]; then
@ -3023,8 +3230,16 @@ else
LOG_FILE="$LOGFILE" LOG_FILE="$LOGFILE"
fi fi
fi
if [ ! -w "$(dirname $LOG_FILE)" ]; then
echo "Cannot write to log [$(dirname $LOG_FILE)]."
else
Logger "Script begin, logging to [$LOG_FILE]." "DEBUG"
fi
if [ "$IS_STABLE" != "yes" ]; then if [ "$IS_STABLE" != "yes" ]; then
Logger "This is an unstable dev build. Please use with caution." "WARN" Logger "This is an unstable dev build [$PROGRAM_BUILD]. Please use with caution." "WARN"
fi fi
DATE=$(date) DATE=$(date)
@ -3035,10 +3250,10 @@ Logger "Backup instance [$INSTANCE_ID] launched as $LOCAL_USER@$LOCAL_HOST (PID
GetLocalOS GetLocalOS
InitLocalOSSettings InitLocalOSSettings
CheckEnvironment
CheckRunningInstances CheckRunningInstances
PreInit PreInit
Init Init
CheckEnvironment
PostInit PostInit
CheckCurrentConfig CheckCurrentConfig