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