This commit is contained in:
Shadowigor 2016-08-28 17:50:53 +02:00
commit dba871e9bf
20 changed files with 393 additions and 473 deletions

View File

@ -6,12 +6,25 @@ KNOWN ISSUES
RECENT CHANGES RECENT CHANGES
-------------- --------------
- Logs sent by mail are easier to read
- Better subject (currently running or finished run)
- Fixed bogus double log sent in alert mails
- Made unix signals posix compliant
- Config file upgrade script now updates header
! test if waitfortaskcompletion kill self works okay with osync
- Improved batch runner
- Made keep logging value configurable and not mandatory
- Fixed handling of processes in uninterruptible sleep state
! update doc on sudoers paths ! update doc on sudoers paths
- Parallelized sync functions - Parallelized sync functions
#TODO: explain bandwidth parameter, and CONFLICT_PREVALANCE option - Rewrite sync resume process
! Remove conflict prevalance
- !doc about bandwidth
- Added options to ignore permissions, ownership and groups - Added options to ignore permissions, ownership and groups
- Refactored WaitFor... functions into one - Refactored WaitFor... functions into one
- Improved execution speed - Improved execution speed
- Rewrite sync resume process
- Added parallel execution for most secondary fuctions - Added parallel execution for most secondary fuctions
- Lowered sleep time in wait functions - Lowered sleep time in wait functions
- Removed trivial sleep and forking in remote deletion code, send the whole function to background instead - Removed trivial sleep and forking in remote deletion code, send the whole function to background instead
@ -20,7 +33,9 @@ RECENT CHANGES
- Added KillAllChilds function to accept multiple pids - Added KillAllChilds function to accept multiple pids
- Improved logging - Improved logging
XX xxx 2016: osync v1.1.2 released 28 Aug 2016: osync v1.1.2 released
- Renamed sync.conf to sync.conf.example (thanks to https://github.com/hortimech)
- Fixed RunAfterHook may be executed twice
- Fixed soft deletion when SUDO_EXEC is enabled - Fixed soft deletion when SUDO_EXEC is enabled
06 Aug 2016: osync v1.1.1 released 06 Aug 2016: osync v1.1.1 released

View File

@ -1,4 +1,4 @@
Coding style used for my bash projects (v2.1 Oct 2015) Coding style used for my bash projects (v2.3 Sep 2016)
++++++ Header ++++++ Header
@ -8,7 +8,7 @@ Always use the following header
#!/usr/bin/env bash #!/usr/bin/env bash
PROGRAM="program-name" # Long description PROGRAM="program-name" # Long description
AUTHOR="(C) 20XX-20YY by Orsiris \"Ozy\" de Jong" AUTHOR="(C) 20XX-20YY by Orsiris de Jong"
CONTACT="http://www.example.com me@example.com" CONTACT="http://www.example.com me@example.com"
PROGRAM_BUILD=YYYYMMDDVV PROGRAM_BUILD=YYYYMMDDVV
@ -23,8 +23,8 @@ YYYYMMDDVV (Year, Month, Day, Revision): Example: 2015012402 = 2nd revision of 2
Change old scripts with Change old scripts with
for i in $(grep -r '#!/bin/bash' * |cut -f1 -d':'); do sed -i 's&#!/bin/bash&#!/usr/bin/env bash&g' $i; done for i in $(grep -r '#!/bin/bash' * |cut -f1 -d':'); do sed -i 's&#!/bin/bash&#!/usr/bin/env bash&g' $i; done
type instead of type -p for bash test (other shells don't know -p) type instead of type -p for bash test (other shells don't know -p)
++++++ Indentation ++++++ Indentation
Using tabs Using tabs
@ -38,59 +38,78 @@ Some command # comment
++++++ Work comments ++++++ Work comments
Whenever there is some idea to postpone, use #TODO[-version]:[dev-name:] some remark Whenever there is some idea to postpone, use #TODO(priority):[dev-name:] some remark
A marker must be left where on the line a dev is working (when the work isn't finished). Marker is #WIP:dev-name: some remark Priority can be critical, high, medium, low, verylow. No release can happen if there are TODOs other than low or verylow.
Example: #TODO(high):deajan: need to do something
A "work in progress" marker must be left on the line a dev is working when it's work isn't finished). Marker is #WIP:dev-name: some remark
dev-name is mandatory if more than one person is coding dev-name is mandatory if more than one person is coding
Example: #TODO-v2.1:deajan: need to do something Example: #WIP:deajan: missing function something
++++++ Variables ++++++ Variables
All local variables are lowercase, separated by _ (ex: low_wait) All local variables names have each first letter of the words uppercase and all others lowercase, except for the first word where all letters are lowercase
All global variables full upercase, separated by _ (ex: EXEC_TIME) Example: someLongVariable
All environment variables (verbose, silent, debug, etc) have prefix _ and are full upercase, separated by _ (ex: _PARANOIA_DEBUG) All global variables are full upercase, separated by _
Example: EXEC_TIME
All environment variables (verbose, silent, debug, etc) have prefix _ and are full upercase, separated by _
Example: _PARANOIA_DEBUG
++++++ Functions ++++++ Functions
Every word in a function begins with an uppercase (ex: SomeFunctionDoesThings) All function names should begin with an uppercase letter for every word, the other letters should be lowercase
Example: SomeFunctionThatRocks
Define functions this way. Use sed ':a;N;$!ba;s/\n{\n/ {\n/g' to adapt when opening bracket is on a new line. Bash does not provide any checks against missing function arguments. Also, missing quotes can lead to an inconsistent number of arguments.
Most functions should have a first line that calls the special function __CheckArguments, which checks the number of given arguments for a function in order
to find possible problems. Number of arguments are given as first argument to __CheckArguments. May be a number or a range, eg 0-2 if the function takes optional arguments.
__CheckArguments will only trigger when the script is launched with _PARANOIA_DEBUG=yes. Also, it will only exist in the debug version.
Use the following convention for function definition:
function something { function SomeFunction {
__CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
...
} }
If function has some arguments, use local variable names that are more readable than $1...$n. Explain via comments what those variables contain if needed. Use sed ':a;N;$!ba;s/\n{\n/ {\n/g' to convert functions that have opening brackets on a new line.
function anotherthing { If the function has arguments, use local variable names that are more readable than $1...$n. Explain via comments what those variables contain if needed.
local var_name="${1}" Declare arguments before launching __CheckArguments:
local other_var_name="${2}" # This variable contains stuff
function AnotherFunction {
local varName="${1}"
local otherVarName="${2}" # This variable contains stuff
__CheckArguments 2 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
...
} }
Functions should always have return status Functions should always have return status
function thirdthing { function RandomFunction {
some_command ...
return $? return $?
} }
++++++ Sub functions ++++++ Sub functions
When a function is a subroutine of another function, it is called _SomethingAsSubFunction When a function is a subroutine of another function, it is called _SomethingAsSubFunction:
Example:
++++++ Function argument check function _ApplyLocally
function _ApplyRemotely
function Apply
Bash does not provide any checks against missing function arguments. Also, missing quotes can lead to an inconsistent number of arguments. ++++++ For and While statements
Every function call will be checked by __CheckArguments which takes the number of arguments, $# (the real number of args given), the parent function name and the parent function's arguments.
__CheckArguments will trigger a critical error if number of arguments if incorrect. This will also prevent silent typo errors.
Ex:
function Something { For and while statements will have the "do" part on the first line.
local some="${1}" Example:
local other="${2}"
local args="${3}"
__CheckArguments 3 $# $FUNCNAME "$*"
__CheckArguments will only trigger if script is called with DEBUG=yes for i in "${var[@]}"; do
Also, with PARANOIA_DEBUG=yes, __CheckArguments will recount all arguments given by "$*" and compare. This can mislead if arguments contain spaces. ...
done
while [ $i -eq 1 ]; do
...
done
++++++ If statements ++++++ If statements
@ -106,7 +125,7 @@ fi
A logging function is available with the following levels of logging: A logging function is available with the following levels of logging:
- DEBUG: Only log this when DEBUG flas is set in program. Any command forged for eval should be logged by this. - DEBUG: Only log this when DEBUG flas is set in program. Any command forged for eval instruction should be logged by this.
- NOTICE: Standard messages - NOTICE: Standard messages
- WARN: Requires attention - WARN: Requires attention
- ERROR: Program produced an error but continues execution - ERROR: Program produced an error but continues execution
@ -128,7 +147,7 @@ cmd=$SSH_CMD' "some; commands \"'$VARIABLE'\" some; other; commands" > some_file
++++++ File variables ++++++ File variables
All eval cmd should exit their content to a file called "$RUNDIR/osync.$FUNCNAME.$SCRIPT_PID" All eval cmd should exit their content to a file called "$RUNDIR/osync.$FUNCNAME.$SCRIPT_PID"
Dots are used instead of '_' so variables can be separated with a forbidden char in variables, so they get detected. Dots are used instead of '_' so variables can be separated with a forbidden char in variable names, so the separtors apply as wished.
++++++ Finding code errors ++++++ Finding code errors
@ -154,3 +173,11 @@ function FunctionName {
#__ENDFUNC #__ENDFUNC
These functions are inserted into code that has placeholders like #__FUNC:FuncName These functions are inserted into code that has placeholders like #__FUNC:FuncName
+++++++ Exit codes
Normal exit code = 0
Run with errors exit code = 1
Run with warnings exit code = 2
Wrong shell exit code = 127
Usage function exit code = 128

View File

@ -19,6 +19,9 @@ Osync provides the following capabilities
- Batch runner for multiple sync tasks with rerun option for failed sync tasks - Batch runner for multiple sync tasks with rerun option for failed sync tasks
- ACL synchronization - ACL synchronization
osync is a state synchronizer. This means that it doesn't have to monitor files for changes. Instead, it compares replica lists between runs.
A full run takes about 2 seconds on a local-local replication and about 10 seconds on a local-remote replication.
Disabling some features file like attributes preservation and disk space checks may speed up execution.
osync uses a initiator / target sync schema. It can sync local to local or local to remote directories. By definition, initiator replica is always a local directory on the system osync runs on. osync uses a initiator / target sync schema. It can sync local to local or local to remote directories. By definition, initiator replica is always a local directory on the system osync runs on.
osync uses pidlocks to prevent multiple concurrent sync processes on/to the same initiator / target replica. osync uses pidlocks to prevent multiple concurrent sync processes on/to the same initiator / target replica.
You may launch concurrent sync processes on the same system but only for different initiator replicas. You may launch concurrent sync processes on the same system but only for different initiator replicas.

View File

@ -1,13 +1,13 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#TODO(critical): handle conflict prevalance, especially in sync_attrs function #TODO(critical): handle conflict prevalance, especially in sync_attrs function
#TODO(critical): test new WaitForTaskCompletion behavior with self=true #TODO(critical): writelockfiles remote does not shut execution
PROGRAM="osync" # Rsync based two way sync engine with fault tolerance PROGRAM="osync" # Rsync based two way sync engine with fault tolerance
AUTHOR="(C) 2013-2016 by Orsiris de Jong" AUTHOR="(C) 2013-2016 by Orsiris de Jong"
CONTACT="http://www.netpower.fr/osync - ozy@netpower.fr" CONTACT="http://www.netpower.fr/osync - ozy@netpower.fr"
PROGRAM_VERSION=1.2-dev-parallel PROGRAM_VERSION=1.2-dev-parallel
PROGRAM_BUILD=2016081902 PROGRAM_BUILD=2016082201
IS_STABLE=no IS_STABLE=no
# Function Name Is parallel #__WITH_PARANOIA_DEBUG # Function Name Is parallel #__WITH_PARANOIA_DEBUG
@ -47,7 +47,7 @@ IS_STABLE=no
#### MINIMAL-FUNCTION-SET BEGIN #### #### MINIMAL-FUNCTION-SET BEGIN ####
## FUNC_BUILD=2016081805 ## FUNC_BUILD=2016082204
## BEGIN Generic functions for osync & obackup written in 2013-2016 by Orsiris de Jong - http://www.netpower.fr - ozy@netpower.fr ## BEGIN Generic functions for osync & obackup written in 2013-2016 by Orsiris de Jong - http://www.netpower.fr - ozy@netpower.fr
## type -p does not work on platforms other than linux (bash). If if does not work, always assume output is not a zero exitcode ## type -p does not work on platforms other than linux (bash). If if does not work, always assume output is not a zero exitcode
@ -230,7 +230,7 @@ function KillChilds {
# Try to kill nicely, if not, wait 15 seconds to let Trap actions happen before killing # Try to kill nicely, if not, wait 15 seconds to let Trap actions happen before killing
if ( [ "$self" == true ] && kill -0 $pid > /dev/null 2>&1); then if ( [ "$self" == true ] && kill -0 $pid > /dev/null 2>&1); then
Logger "Sending SIGTERM to process [$pid]." "DEBUG" Logger "Sending SIGTERM to process [$pid]." "DEBUG"
kill -s SIGTERM "$pid" kill -s TERM "$pid"
if [ $? != 0 ]; then if [ $? != 0 ]; then
sleep 15 sleep 15
Logger "Sending SIGTERM to process [$pid] failed." "DEBUG" Logger "Sending SIGTERM to process [$pid] failed." "DEBUG"
@ -646,29 +646,6 @@ function WaitForTaskCompletion {
while [ ${#pidsArray[@]} -gt 0 ]; do while [ ${#pidsArray[@]} -gt 0 ]; do
newPidsArray=() newPidsArray=()
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"
fi
fi
fi
done
Spinner Spinner
if [ $counting == true ]; then if [ $counting == true ]; then
@ -708,6 +685,30 @@ function WaitForTaskCompletion {
fi fi
fi 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"
fi
fi
fi
done
pidsArray=("${newPidsArray[@]}") pidsArray=("${newPidsArray[@]}")
sleep $SLEEP_TIME sleep $SLEEP_TIME
done done
@ -1037,7 +1038,7 @@ function CheckConnectivity3rdPartyHosts {
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" &
WaitForTaskCompletion $! 10 360 ${FUNCNAME[0]} true $KEEP_LOGGING WaitForTaskCompletion $! 180 360 ${FUNCNAME[0]} true $KEEP_LOGGING
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot ping 3rd party host $i" "NOTICE" Logger "Cannot ping 3rd party host $i" "NOTICE"
else else
@ -1417,9 +1418,7 @@ function TrapQuit {
exitcode=2 # Warning exit code must not force daemon mode to quit exitcode=2 # Warning exit code must not force daemon mode to quit
else else
UnlockReplicas UnlockReplicas
if [ "$RUN_AFTER_CMD_ON_ERROR" == "yes" ]; then RunAfterHook
RunAfterHook
fi
CleanUp CleanUp
Logger "$PROGRAM finished." "NOTICE" Logger "$PROGRAM finished." "NOTICE"
exitcode=0 exitcode=0
@ -1541,15 +1540,16 @@ function CheckReplicaPaths {
local pids local pids
# Use direct comparaison before having a portable realpath implementation
#INITIATOR_SYNC_DIR_CANN=$(realpath "${INITIATOR[1]}") #TODO(verylow): investigate realpath & readlink issues on MSYS and busybox here #INITIATOR_SYNC_DIR_CANN=$(realpath "${INITIATOR[1]}") #TODO(verylow): investigate realpath & readlink issues on MSYS and busybox here
#TARGET_SYNC_DIR_CANN=$(realpath "${TARGET[1]}) #TARGET_SYNC_DIR_CANN=$(realpath "${TARGET[1]})
#if [ "$REMOTE_OPERATION" != "yes" ]; then if [ "$REMOTE_OPERATION" != "yes" ]; then
# if [ "$INITIATOR_SYNC_DIR_CANN" == "$TARGET_SYNC_DIR_CANN" ]; then if [ "${INITIATOR[1]}" == "${TARGET[1]}" ]; then
# Logger "Master directory [${INITIATOR[1]}] cannot be the same as target directory." "CRITICAL" Logger "Initiator and target path [${INITIATOR[1]}] cannot be the same." "CRITICAL"
# exit 1 exit 1
# fi fi
#fi fi
_CheckReplicaPathsLocal "${INITIATOR[1]}" & _CheckReplicaPathsLocal "${INITIATOR[1]}" &
pids="$!" pids="$!"
@ -2887,10 +2887,10 @@ function Init {
# Do not use exit and quit traps if osync runs in monitor mode # Do not use exit and quit traps if osync runs in monitor mode
if [ $sync_on_changes -eq 0 ]; then if [ $sync_on_changes -eq 0 ]; then
trap TrapStop SIGINT SIGHUP SIGTERM SIGQUIT trap TrapStop INT HUP TERM QUIT
trap TrapQuit EXIT trap TrapQuit EXIT
else else
trap TrapQuit SIGTERM EXIT SIGHUP SIGQUIT trap TrapQuit TERM EXIT HUP QUIT
fi fi
local uri local uri

View File

@ -1,13 +1,13 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#TODO(critical): handle conflict prevalance, especially in sync_attrs function #TODO(critical): handle conflict prevalance, especially in sync_attrs function
#TODO(critical): test new WaitForTaskCompletion behavior with self=true #TODO(critical): writelockfiles remote does not shut execution
PROGRAM="osync" # Rsync based two way sync engine with fault tolerance PROGRAM="osync" # Rsync based two way sync engine with fault tolerance
AUTHOR="(C) 2013-2016 by Orsiris de Jong" AUTHOR="(C) 2013-2016 by Orsiris de Jong"
CONTACT="http://www.netpower.fr/osync - ozy@netpower.fr" CONTACT="http://www.netpower.fr/osync - ozy@netpower.fr"
PROGRAM_VERSION=1.2-dev-parallel PROGRAM_VERSION=1.2-dev-parallel
PROGRAM_BUILD=2016081902 PROGRAM_BUILD=2016082201
IS_STABLE=no IS_STABLE=no
# Function Name Is parallel #__WITH_PARANOIA_DEBUG # Function Name Is parallel #__WITH_PARANOIA_DEBUG
@ -99,9 +99,7 @@ function TrapQuit {
exitcode=2 # Warning exit code must not force daemon mode to quit exitcode=2 # Warning exit code must not force daemon mode to quit
else else
UnlockReplicas UnlockReplicas
if [ "$RUN_AFTER_CMD_ON_ERROR" == "yes" ]; then RunAfterHook
RunAfterHook
fi
CleanUp CleanUp
Logger "$PROGRAM finished." "NOTICE" Logger "$PROGRAM finished." "NOTICE"
exitcode=0 exitcode=0
@ -223,15 +221,16 @@ function CheckReplicaPaths {
local pids local pids
# Use direct comparaison before having a portable realpath implementation
#INITIATOR_SYNC_DIR_CANN=$(realpath "${INITIATOR[1]}") #TODO(verylow): investigate realpath & readlink issues on MSYS and busybox here #INITIATOR_SYNC_DIR_CANN=$(realpath "${INITIATOR[1]}") #TODO(verylow): investigate realpath & readlink issues on MSYS and busybox here
#TARGET_SYNC_DIR_CANN=$(realpath "${TARGET[1]}) #TARGET_SYNC_DIR_CANN=$(realpath "${TARGET[1]})
#if [ "$REMOTE_OPERATION" != "yes" ]; then if [ "$REMOTE_OPERATION" != "yes" ]; then
# if [ "$INITIATOR_SYNC_DIR_CANN" == "$TARGET_SYNC_DIR_CANN" ]; then if [ "${INITIATOR[1]}" == "${TARGET[1]}" ]; then
# Logger "Master directory [${INITIATOR[1]}] cannot be the same as target directory." "CRITICAL" Logger "Initiator and target path [${INITIATOR[1]}] cannot be the same." "CRITICAL"
# exit 1 exit 1
# fi fi
#fi fi
_CheckReplicaPathsLocal "${INITIATOR[1]}" & _CheckReplicaPathsLocal "${INITIATOR[1]}" &
pids="$!" pids="$!"
@ -1569,10 +1568,10 @@ function Init {
# Do not use exit and quit traps if osync runs in monitor mode # Do not use exit and quit traps if osync runs in monitor mode
if [ $sync_on_changes -eq 0 ]; then if [ $sync_on_changes -eq 0 ]; then
trap TrapStop SIGINT SIGHUP SIGTERM SIGQUIT trap TrapStop INT HUP TERM QUIT
trap TrapQuit EXIT trap TrapQuit EXIT
else else
trap TrapQuit SIGTERM EXIT SIGHUP SIGQUIT trap TrapQuit TERM EXIT HUP QUIT
fi fi
local uri local uri

View File

@ -1,9 +1,13 @@
#### MINIMAL-FUNCTION-SET BEGIN #### #### MINIMAL-FUNCTION-SET BEGIN ####
## FUNC_BUILD=2016081806 ## FUNC_BUILD=2016082801
## BEGIN Generic functions for osync & obackup written in 2013-2016 by Orsiris de Jong - http://www.netpower.fr - ozy@netpower.fr ## BEGIN Generic bash functions written in 2013-2016 by Orsiris de Jong - http://www.netpower.fr - ozy@netpower.fr
## To use in a program, define the following variables:
## PROGRAM=program-name
## INSTANCE_ID=program-instance-name
## _DEBUG=yes/no
## type -p does not work on platforms other than linux (bash). If if does not work, always assume output is not a zero exitcode
if ! type "$BASH" > /dev/null; then if ! type "$BASH" > /dev/null; then
echo "Please run this script only with bash shell. Tested on bash >= 3.2" echo "Please run this script only with bash shell. Tested on bash >= 3.2"
exit 127 exit 127
@ -28,6 +32,9 @@ fi
ERROR_ALERT=0 ERROR_ALERT=0
WARN_ALERT=0 WARN_ALERT=0
# Current log
CURRENT_LOG=
## allow function call checks #__WITH_PARANOIA_DEBUG ## allow function call checks #__WITH_PARANOIA_DEBUG
if [ "$_PARANOIA_DEBUG" == "yes" ];then #__WITH_PARANOIA_DEBUG if [ "$_PARANOIA_DEBUG" == "yes" ];then #__WITH_PARANOIA_DEBUG
_DEBUG=yes #__WITH_PARANOIA_DEBUG _DEBUG=yes #__WITH_PARANOIA_DEBUG
@ -49,6 +56,10 @@ SCRIPT_PID=$$
LOCAL_USER=$(whoami) LOCAL_USER=$(whoami)
LOCAL_HOST=$(hostname) LOCAL_HOST=$(hostname)
if [ "$PROGRAM" == "" ]; then
PROGRAM="ofunctions"
fi
## Default log file until config file is loaded ## Default log file until config file is loaded
if [ -w /var/log ]; then if [ -w /var/log ]; then
LOG_FILE="/var/log/$PROGRAM.log" LOG_FILE="/var/log/$PROGRAM.log"
@ -89,6 +100,7 @@ function _Logger {
local evalue="${3}" # What to log to stderr local evalue="${3}" # What to log to stderr
echo -e "$lvalue" >> "$LOG_FILE" echo -e "$lvalue" >> "$LOG_FILE"
CURRENT_LOG="$CURRENT_LOG"$'\n'"$lvalue"
if [ "$_LOGGER_STDERR" -eq 1 ]; then if [ "$_LOGGER_STDERR" -eq 1 ]; then
cat <<< "$evalue" 1>&2 cat <<< "$evalue" 1>&2
@ -183,7 +195,7 @@ function KillChilds {
# Try to kill nicely, if not, wait 15 seconds to let Trap actions happen before killing # Try to kill nicely, if not, wait 15 seconds to let Trap actions happen before killing
if ( [ "$self" == true ] && kill -0 $pid > /dev/null 2>&1); then if ( [ "$self" == true ] && kill -0 $pid > /dev/null 2>&1); then
Logger "Sending SIGTERM to process [$pid]." "DEBUG" Logger "Sending SIGTERM to process [$pid]." "DEBUG"
kill -s SIGTERM "$pid" kill -s TERM "$pid"
if [ $? != 0 ]; then if [ $? != 0 ]; then
sleep 15 sleep 15
Logger "Sending SIGTERM to process [$pid] failed." "DEBUG" Logger "Sending SIGTERM to process [$pid] failed." "DEBUG"
@ -220,11 +232,14 @@ function KillAllChilds {
# osync/obackup/pmocr script specific mail alert function, use SendEmail function for generic mail sending # osync/obackup/pmocr script specific mail alert function, use SendEmail function for generic mail sending
function SendAlert { function SendAlert {
__CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG local runAlert="${1:-false}" # Specifies if current message is sent while running or at the end of a run
__CheckArguments 0-1 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
local mail_no_attachment= local mail_no_attachment=
local attachment_command= local attachment_command=
local subject= local subject=
local body=
# Windows specific settings # Windows specific settings
local encryption_string= local encryption_string=
@ -253,7 +268,8 @@ function SendAlert {
else else
mail_no_attachment=0 mail_no_attachment=0
fi fi
MAIL_ALERT_MSG="$MAIL_ALERT_MSG"$'\n\n'$(tail -n 50 "$LOG_FILE") body="$MAIL_ALERT_MSG"$'\n\n'"$CURRENT_LOG"
if [ $ERROR_ALERT -eq 1 ]; then if [ $ERROR_ALERT -eq 1 ]; then
subject="Error alert for $INSTANCE_ID" subject="Error alert for $INSTANCE_ID"
elif [ $WARN_ALERT -eq 1 ]; then elif [ $WARN_ALERT -eq 1 ]; then
@ -262,11 +278,17 @@ function SendAlert {
subject="Alert for $INSTANCE_ID" subject="Alert for $INSTANCE_ID"
fi fi
if [ $runAlert == true ]; then
subject="Currently runing - $subject"
else
subject="Fnished run - $subject"
fi
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 type mutt > /dev/null 2>&1 ; then if type mutt > /dev/null 2>&1 ; then
echo "$MAIL_ALERT_MSG" | $(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
Logger "Cannot send alert mail via $(type -p mutt) !!!" "WARN" Logger "Cannot send alert mail via $(type -p mutt) !!!" "WARN"
else else
@ -283,10 +305,10 @@ function SendAlert {
else else
attachment_command="" attachment_command=""
fi fi
echo "$MAIL_ALERT_MSG" | $(type -p mail) $attachment_command -s "$subject" $DESTINATION_MAILS echo "$body" | $(type -p mail) $attachment_command -s "$subject" $DESTINATION_MAILS
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send alert mail via $(type -p mail) with attachments !!!" "WARN" Logger "Cannot send alert mail via $(type -p mail) with attachments !!!" "WARN"
echo "$MAIL_ALERT_MSG" | $(type -p mail) -s "$subject" $DESTINATION_MAILS echo "$body" | $(type -p mail) -s "$subject" $DESTINATION_MAILS
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send alert mail via $(type -p mail) without attachments !!!" "WARN" Logger "Cannot send alert mail via $(type -p mail) without attachments !!!" "WARN"
else else
@ -300,7 +322,7 @@ function SendAlert {
fi fi
if type sendmail > /dev/null 2>&1 ; then if type sendmail > /dev/null 2>&1 ; then
echo -e "Subject:$subject\r\n$MAIL_ALERT_MSG" | $(type -p sendmail) $DESTINATION_MAILS echo -e "Subject:$subject\r\n$body" | $(type -p sendmail) $DESTINATION_MAILS
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send alert mail via $(type -p sendmail) !!!" "WARN" Logger "Cannot send alert mail via $(type -p sendmail) !!!" "WARN"
else else
@ -323,7 +345,7 @@ function SendAlert {
if [ "$SMTP_USER" != "" ] && [ "$SMTP_USER" != "" ]; then if [ "$SMTP_USER" != "" ] && [ "$SMTP_USER" != "" ]; then
auth_string="-auth -user \"$SMTP_USER\" -pass \"$SMTP_PASSWORD\"" auth_string="-auth -user \"$SMTP_USER\" -pass \"$SMTP_PASSWORD\""
fi fi
$(type mailsend.exe) -f $SENDER_MAIL -t "$DESTINATION_MAILS" -sub "$subject" -M "$MAIL_ALERT_MSG" -attach "$attachment" -smtp "$SMTP_SERVER" -port "$SMTP_PORT" $encryption_string $auth_string $(type mailsend.exe) -f $SENDER_MAIL -t "$DESTINATION_MAILS" -sub "$subject" -M "$body" -attach "$attachment" -smtp "$SMTP_SERVER" -port "$SMTP_PORT" $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
@ -339,7 +361,7 @@ function SendAlert {
else else
SMTP_OPTIONS="" SMTP_OPTIONS=""
fi fi
$(type -p sendemail) -f $SENDER_MAIL -t "$DESTINATION_MAILS" -u "$subject" -m "$MAIL_ALERT_MSG" -s $SMTP_SERVER $SMTP_OPTIONS > /dev/null 2>&1 $(type -p sendemail) -f $SENDER_MAIL -t "$DESTINATION_MAILS" -u "$subject" -m "$body" -s $SMTP_SERVER $SMTP_OPTIONS > /dev/null 2>&1
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send alert mail via $(type -p sendemail) !!!" "WARN" Logger "Cannot send alert mail via $(type -p sendemail) !!!" "WARN"
else else
@ -350,7 +372,7 @@ function SendAlert {
# pfSense specific # pfSense specific
if [ -f /usr/local/bin/mail.php ]; then if [ -f /usr/local/bin/mail.php ]; then
echo "$MAIL_ALERT_MSG" | /usr/local/bin/mail.php -s="$subject" echo "$body" | /usr/local/bin/mail.php -s="$subject"
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot send alert mail via /usr/local/bin/mail.php (pfsense) !!!" "WARN" Logger "Cannot send alert mail via /usr/local/bin/mail.php (pfsense) !!!" "WARN"
else else
@ -569,6 +591,8 @@ function joinString {
# Time control function for background processes, suitable for multiple synchronous processes # Time control function for background processes, suitable for multiple synchronous processes
# Fills a global variable called WAIT_FOR_TASK_COMPLETION that contains list of failed pids in format pid1:result1;pid2:result2 # Fills a global variable called WAIT_FOR_TASK_COMPLETION that contains list of failed pids in format pid1:result1;pid2:result2
# Warning: Don't imbricate this function into another run if you plan to use the global variable output # Warning: Don't imbricate this function into another run if you plan to use the global variable output
#TODO check missing local values used here
function WaitForTaskCompletion { function WaitForTaskCompletion {
local pids="${1}" # pids to wait for, separated by semi-colon local pids="${1}" # pids to wait for, separated by semi-colon
local soft_max_time="${2}" # If program with pid $pid takes longer than $soft_max_time seconds, will log a warning, unless $soft_max_time equals 0. local soft_max_time="${2}" # If program with pid $pid takes longer than $soft_max_time seconds, will log a warning, unless $soft_max_time equals 0.
@ -599,29 +623,6 @@ function WaitForTaskCompletion {
while [ ${#pidsArray[@]} -gt 0 ]; do while [ ${#pidsArray[@]} -gt 0 ]; do
newPidsArray=() newPidsArray=()
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"
fi
fi
fi
done
Spinner Spinner
if [ $counting == true ]; then if [ $counting == true ]; then
@ -643,7 +644,7 @@ function WaitForTaskCompletion {
if [ $soft_alert -eq 0 ] && [ $soft_max_time -ne 0 ]; then if [ $soft_alert -eq 0 ] && [ $soft_max_time -ne 0 ]; then
Logger "Max soft execution time exceeded for task [$caller_name] with pids [$(joinString , ${pidsArray[@]})]." "WARN" Logger "Max soft execution time exceeded for task [$caller_name] with pids [$(joinString , ${pidsArray[@]})]." "WARN"
soft_alert=1 soft_alert=1
SendAlert SendAlert true
fi fi
if [ $exec_time -gt $hard_max_time ] && [ $hard_max_time -ne 0 ]; then if [ $exec_time -gt $hard_max_time ] && [ $hard_max_time -ne 0 ]; then
@ -656,12 +657,37 @@ function WaitForTaskCompletion {
Logger "Could not stop task with pid [$pid]." "ERROR" Logger "Could not stop task with pid [$pid]." "ERROR"
fi fi
done done
SendAlert SendAlert true
errrorcount=$((errorcount+1)) errrorcount=$((errorcount+1))
fi fi
fi 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 [$retval]." "DEBUG"
if [ "$WAIT_FOR_TASK_COMPLETION" == "" ]; then
WAIT_FOR_TASK_COMPLETION="$pid:$retval"
else
WAIT_FOR_TASK_COMPLETION=";$pid:$retval"
fi
fi
fi
done
pidsArray=("${newPidsArray[@]}") pidsArray=("${newPidsArray[@]}")
# Trivial wait time for bash to not eat up all CPU
sleep $SLEEP_TIME sleep $SLEEP_TIME
done done
@ -675,6 +701,66 @@ function WaitForTaskCompletion {
fi fi
} }
# Take a list of commands to run, runs them sequentially with numberOfProcesses commands simultaneously runs
# Returns the number of non zero exit codes from commands
function ParallelExec {
local numberOfProcesses="${1}" # Number of simultaneous commands to run
local commandsArg="${2}" # Semi-colon separated list of commands
local pid
local runningPids=0
local counter=0
local commandsArray
local pidsArray
local newPidsArray
local retval
local retvalAll=0
local pidState
local commandsArrayPid
IFS=';' read -r -a commandsArray <<< "$commandsArg"
Logger "Runnning ${#commandsArray[@]} commands in $numberOfProcesses simultaneous processes." "DEBUG"
while [ $counter -lt "${#commandsArray[@]}" ] || [ ${#pidsArray[@]} -gt 0 ]; do
while [ $counter -lt "${#commandsArray[@]}" ] && [ ${#pidsArray[@]} -lt $numberOfProcesses ]; do
Logger "Running command [${commandsArray[$counter]}]." "DEBUG"
eval "${commandsArray[$counter]}" &
pid=$!
pidsArray+=($pid)
commandsArrayPid[$pid]="${commandsArray[$counter]}"
counter=$((counter+1))
done
newPidsArray=()
for pid in "${pidsArray[@]}"; do
# 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
done
pidsArray=("${newPidsArray[@]}")
# Trivial wait time for bash to not eat up all CPU
sleep $SLEEP_TIME
done
return $retvalAll
}
function CleanUp { function CleanUp {
__CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
@ -968,7 +1054,7 @@ function CheckConnectivityRemoteHost {
if [ "$REMOTE_HOST_PING" != "no" ] && [ "$REMOTE_OPERATION" != "no" ]; then if [ "$REMOTE_HOST_PING" != "no" ] && [ "$REMOTE_OPERATION" != "no" ]; then
eval "$PING_CMD $REMOTE_HOST > /dev/null 2>&1" & eval "$PING_CMD $REMOTE_HOST > /dev/null 2>&1" &
WaitForTaskCompletion $! 10 180 ${FUNCNAME[0]} true $KEEP_LOGGING WaitForTaskCompletion $! 60 180 ${FUNCNAME[0]} true $KEEP_LOGGING
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot ping $REMOTE_HOST" "ERROR" Logger "Cannot ping $REMOTE_HOST" "ERROR"
return 1 return 1
@ -1013,12 +1099,15 @@ function __CheckArguments {
# Checks the number of arguments of a function and raises an error if some are missing # Checks the number of arguments of a function and raises an error if some are missing
if [ "$_DEBUG" == "yes" ]; then if [ "$_DEBUG" == "yes" ]; then
local number_of_arguments="${1}" # Number of arguments the tested function should have local numberOfArguments="${1}" # Number of arguments the tested function should have, can be a number of a range, eg 0-2 for zero to two arguments
local number_of_given_arguments="${2}" # Number of arguments that have been passed local numberOfGivenArguments="${2}" # Number of arguments that have been passed
local function_name="${3}" # Function name that called __CheckArguments local functionName="${3}" # Function name that called __CheckArguments
local minArgs
local maxArgs
if [ "$_PARANOIA_DEBUG" == "yes" ]; then if [ "$_PARANOIA_DEBUG" == "yes" ]; then
Logger "Entering function [$function_name]." "DEBUG" Logger "Entering function [$functionName]." "DEBUG"
fi fi
# All arguments of the function to check are passed as array in ${4} (the function call waits for $@) # All arguments of the function to check are passed as array in ${4} (the function call waits for $@)
@ -1026,23 +1115,31 @@ function __CheckArguments {
# In order to avoid this, we need to iterate over ${4} and count # In order to avoid this, we need to iterate over ${4} and count
local iterate=4 local iterate=4
local fetch_arguments=1 local fetchArguments=1
local arg_list="" local argList=""
while [ $fetch_arguments -eq 1 ]; do local countedArguments
while [ $fetchArguments -eq 1 ]; do
cmd='argument=${'$iterate'}' cmd='argument=${'$iterate'}'
eval $cmd eval $cmd
if [ "$argument" = "" ]; then if [ "$argument" = "" ]; then
fetch_arguments=0 fetchArguments=0
else else
arg_list="$arg_list [Argument $(($iterate-3)): $argument]" argList="$arg_list [Argument $(($iterate-3)): $argument]"
iterate=$(($iterate+1)) iterate=$(($iterate+1))
fi fi
done done
local counted_arguments=$((iterate-4)) countedArguments=$((iterate-4))
if [ $counted_arguments -ne $number_of_arguments ]; then if [ $(IsNumeric "$numberOfArguments") -eq 1 ]; then
Logger "Function $function_name may have inconsistent number of arguments. Expected: $number_of_arguments, count: $counted_arguments, bash seen: $number_of_given_arguments. see log file." "ERROR" minArgs=$numberOfArguments
Logger "Arguments passed: $arg_list" "ERROR" maxArgs=$numberOfArguments
else
IFS='-' read minArgs maxArgs <<< "$numberOfArguments"
fi
if ! ([ $countedArguments -ge $minArgs ] && [ $countedArguments -le $maxArgs ]); then
Logger "Function $functionName may have inconsistent number of arguments. Expected min: $minArgs, max: $maxArgs, count: $countedArguments, bash seen: $numberOfGivenArguments. see log file." "ERROR"
Logger "Arguments passed: $argList" "ERROR"
fi fi
fi fi
} }
@ -1286,7 +1383,6 @@ function InitLocalOSSettings {
function InitRemoteOSSettings { function InitRemoteOSSettings {
__CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG __CheckArguments 0 $# ${FUNCNAME[0]} "$@" #__WITH_PARANOIA_DEBUG
#TODO: fix add -E when both initiator and targets don't run MacOSX and PRESERVE_EXECUTABILITY=yes
## MacOSX does not use the -E parameter like Linux or BSD does (-E is mapped to extended attrs instead of preserve executability) ## MacOSX does not use the -E parameter like Linux or BSD does (-E is mapped to extended attrs instead of preserve executability)
if [ "$PRESERVE_EXECUTABILITY" != "no" ];then if [ "$PRESERVE_EXECUTABILITY" != "no" ];then
if [ "$LOCAL_OS" != "MacOSX" ] && [ "$REMOTE_OS" != "MacOSX" ]; then if [ "$LOCAL_OS" != "MacOSX" ] && [ "$REMOTE_OS" != "MacOSX" ]; then

View File

@ -1,9 +1,18 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# osync shunit2 tests
# modify ctime on ext4
debugfs -w -R 'set_inode_field /tmp/foo ctime 201001010101' /dev/sda1
echo > /proc/sys/vm/drop_caches
# Test dir # Test dir
TMP="/tmp/osync_tests" TMP="/tmp/osync_tests"
# SSH port used for remote tests # SSH port used for remote tests
SSH_PORT=49999 SSH_PORT=22
INITIATOR_DIR="init"
TARGET_DIR="targ"
# Get dir the tests are stored in # Get dir the tests are stored in
TEST_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) TEST_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
@ -11,8 +20,8 @@ cd "$TEST_DIR"
OSYNC_EXECUTABLE="$(dirname $TEST_DIR)//osync.sh" OSYNC_EXECUTABLE="$(dirname $TEST_DIR)//osync.sh"
declare -A sandbox_osync declare -A sandbox_osync
#sandbox_osync[quickLocal]="--master=master --slave=slave" sandbox_osync[quickLocal]="--initiator=$INITIATOR_DIR --target=$TARGET_DIR"
#sandbox_osync[quickRemote]="--master=master --slave=ssh://localhost//tmp/osync_tests/quickRemote/slave" #sandbox_osync[quickRemote]="--initiator=$INITIATOR_DIR --slave=ssh://localhost:$SSH_PORT/$TMP/$TARGET_DIR"
#sandbox_osync[local]="conf/local.conf" #sandbox_osync[local]="conf/local.conf"
#sandbox_osync[remote]="conf/remote.conf" #sandbox_osync[remote]="conf/remote.conf"
@ -34,8 +43,8 @@ prepareSandbox()
rm -rf "$TMP/$1" rm -rf "$TMP/$1"
mkdir -p "$TMP/$1" mkdir -p "$TMP/$1"
pushd "$TMP/$1" >/dev/null pushd "$TMP/$1" >/dev/null
mkdir master mkdir "$INITIATOR_DIR"
mkdir slave mkdir "$TARGET_DIR"
mkdir expected mkdir expected
popd >/dev/null popd >/dev/null
} }

54
dev/tests/run_tests.sh Executable file
View File

@ -0,0 +1,54 @@
#!/usr/bin/env bash
# osync test suite 2016081901
DEV_DIR="/home/git/osync/dev"
OSYNC_EXECUTABLE="n_osync.sh"
INITIATOR_DIR="/opt/osync/initiator"
TARGET_DIR="/opt/osync/target"
OSYNC_STATE_DIR=".osync_workdir/state"
function CreateReplicas () {
if [ -d "$INITIATOR_DIR" ]; then
rm -rf "$INITIATOR_DIR"
fi
mkdir -p "$INITIATOR_DIR"
if [ -d "$TARGET_DIR" ]; then
rm -rf "$TARGET_DIR"
fi
mkdir -p "$TARGET_DIR"
}
function oneTimeSetUp () {
source "$DEV_DIR/ofunctions.sh"
if grep "^IS_STABLE=YES" "$DEV_DIR/$OSYNC_EXECUTABLE" > /dev/null; then
IS_STABLE=yes
else
IS_STABLE=no
sed -i 's/^IS_STABLE=no/IS_STABLE=yes/' "$DEV_DIR/$OSYNC_EXECUTABLE"
fi
}
function oneTimeTearDown () {
if [ "$IS_STABLE" == "no" ]; then
sed -i 's/^IS_STABLE=yes/IS_STABLE=no/' "$DEV_DIR/$OSYNC_EXECUTABLE"
fi
}
function test_osync_quicksync_local () {
CreateReplicas
cd "$DEV_DIR"
./n_osync.sh --initiator="$INITIATOR_DIR" --target="$TARGET_DIR" > /dev/null
assertEquals "Return code" "0" $?
[ -d "$INITIATOR_DIR/$OSYNC_STATE_DIR" ]
assertEquals "Initiator state dir exists" "0" $?
[ -d "$TARGET_DIR/$OSYNC_STATE_DIR" ]
assertEquals "Target state dir exists" "0" $?
}
. ./shunit2/shunit2

View File

@ -1,20 +1,20 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#TODO(critical): handle conflict prevalance, especially in sync_attrs function #TODO(critical): handle conflict prevalance, especially in sync_attrs function
#TODO(critical): test new WaitForTaskCompletion behavior with self=true #TODO(critical): writelockfiles remote does not shut execution
PROGRAM="osync" # Rsync based two way sync engine with fault tolerance PROGRAM="osync" # Rsync based two way sync engine with fault tolerance
AUTHOR="(C) 2013-2016 by Orsiris de Jong" AUTHOR="(C) 2013-2016 by Orsiris de Jong"
CONTACT="http://www.netpower.fr/osync - ozy@netpower.fr" CONTACT="http://www.netpower.fr/osync - ozy@netpower.fr"
PROGRAM_VERSION=1.2-dev-parallel PROGRAM_VERSION=1.2-dev-parallel
PROGRAM_BUILD=2016081902 PROGRAM_BUILD=2016082201
IS_STABLE=no IS_STABLE=no
#### MINIMAL-FUNCTION-SET BEGIN #### #### MINIMAL-FUNCTION-SET BEGIN ####
## FUNC_BUILD=2016081805 ## FUNC_BUILD=2016082204
## BEGIN Generic functions for osync & obackup written in 2013-2016 by Orsiris de Jong - http://www.netpower.fr - ozy@netpower.fr ## BEGIN Generic functions for osync & obackup written in 2013-2016 by Orsiris de Jong - http://www.netpower.fr - ozy@netpower.fr
## type -p does not work on platforms other than linux (bash). If if does not work, always assume output is not a zero exitcode ## type -p does not work on platforms other than linux (bash). If if does not work, always assume output is not a zero exitcode
@ -184,7 +184,7 @@ function KillChilds {
# Try to kill nicely, if not, wait 15 seconds to let Trap actions happen before killing # Try to kill nicely, if not, wait 15 seconds to let Trap actions happen before killing
if ( [ "$self" == true ] && kill -0 $pid > /dev/null 2>&1); then if ( [ "$self" == true ] && kill -0 $pid > /dev/null 2>&1); then
Logger "Sending SIGTERM to process [$pid]." "DEBUG" Logger "Sending SIGTERM to process [$pid]." "DEBUG"
kill -s SIGTERM "$pid" kill -s TERM "$pid"
if [ $? != 0 ]; then if [ $? != 0 ]; then
sleep 15 sleep 15
Logger "Sending SIGTERM to process [$pid] failed." "DEBUG" Logger "Sending SIGTERM to process [$pid] failed." "DEBUG"
@ -594,29 +594,6 @@ function WaitForTaskCompletion {
while [ ${#pidsArray[@]} -gt 0 ]; do while [ ${#pidsArray[@]} -gt 0 ]; do
newPidsArray=() newPidsArray=()
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"
fi
fi
fi
done
Spinner Spinner
if [ $counting == true ]; then if [ $counting == true ]; then
@ -656,6 +633,30 @@ function WaitForTaskCompletion {
fi fi
fi 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"
fi
fi
fi
done
pidsArray=("${newPidsArray[@]}") pidsArray=("${newPidsArray[@]}")
sleep $SLEEP_TIME sleep $SLEEP_TIME
done done
@ -975,7 +976,7 @@ function CheckConnectivity3rdPartyHosts {
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" &
WaitForTaskCompletion $! 10 360 ${FUNCNAME[0]} true $KEEP_LOGGING WaitForTaskCompletion $! 180 360 ${FUNCNAME[0]} true $KEEP_LOGGING
if [ $? != 0 ]; then if [ $? != 0 ]; then
Logger "Cannot ping 3rd party host $i" "NOTICE" Logger "Cannot ping 3rd party host $i" "NOTICE"
else else
@ -1310,9 +1311,7 @@ function TrapQuit {
exitcode=2 # Warning exit code must not force daemon mode to quit exitcode=2 # Warning exit code must not force daemon mode to quit
else else
UnlockReplicas UnlockReplicas
if [ "$RUN_AFTER_CMD_ON_ERROR" == "yes" ]; then RunAfterHook
RunAfterHook
fi
CleanUp CleanUp
Logger "$PROGRAM finished." "NOTICE" Logger "$PROGRAM finished." "NOTICE"
exitcode=0 exitcode=0
@ -1429,15 +1428,16 @@ function CheckReplicaPaths {
local pids local pids
# Use direct comparaison before having a portable realpath implementation
#INITIATOR_SYNC_DIR_CANN=$(realpath "${INITIATOR[1]}") #TODO(verylow): investigate realpath & readlink issues on MSYS and busybox here #INITIATOR_SYNC_DIR_CANN=$(realpath "${INITIATOR[1]}") #TODO(verylow): investigate realpath & readlink issues on MSYS and busybox here
#TARGET_SYNC_DIR_CANN=$(realpath "${TARGET[1]}) #TARGET_SYNC_DIR_CANN=$(realpath "${TARGET[1]})
#if [ "$REMOTE_OPERATION" != "yes" ]; then if [ "$REMOTE_OPERATION" != "yes" ]; then
# if [ "$INITIATOR_SYNC_DIR_CANN" == "$TARGET_SYNC_DIR_CANN" ]; then if [ "${INITIATOR[1]}" == "${TARGET[1]}" ]; then
# Logger "Master directory [${INITIATOR[1]}] cannot be the same as target directory." "CRITICAL" Logger "Initiator and target path [${INITIATOR[1]}] cannot be the same." "CRITICAL"
# exit 1 exit 1
# fi fi
#fi fi
_CheckReplicaPathsLocal "${INITIATOR[1]}" & _CheckReplicaPathsLocal "${INITIATOR[1]}" &
pids="$!" pids="$!"
@ -2746,10 +2746,10 @@ function Init {
# Do not use exit and quit traps if osync runs in monitor mode # Do not use exit and quit traps if osync runs in monitor mode
if [ $sync_on_changes -eq 0 ]; then if [ $sync_on_changes -eq 0 ]; then
trap TrapStop SIGINT SIGHUP SIGTERM SIGQUIT trap TrapStop INT HUP TERM QUIT
trap TrapQuit EXIT trap TrapQuit EXIT
else else
trap TrapQuit SIGTERM EXIT SIGHUP SIGQUIT trap TrapQuit TERM EXIT HUP QUIT
fi fi
local uri local uri

View File

@ -2,7 +2,7 @@
###### osync - Rsync based two way sync engine with fault tolerance ###### osync - Rsync based two way sync engine with fault tolerance
###### (C) 2013-2016 by Orsiris de Jong (www.netpower.fr) ###### (C) 2013-2016 by Orsiris de Jong (www.netpower.fr)
###### osync v1.1x / v1.2x config file rev 2016080902 ###### osync v1.1x / v1.2x config file rev 2016081901
## ---------- GENERAL OPTIONS ## ---------- GENERAL OPTIONS
@ -80,7 +80,7 @@ REMOTE_3RD_PARTY_HOSTS="www.kernel.org www.google.com"
PRESERVE_PERMISSIONS=yes PRESERVE_PERMISSIONS=yes
PRESERVE_OWNER=yes PRESERVE_OWNER=yes
PRESERVE_GROUP=yes PRESERVE_GROUP=yes
## Does not work and will be ignored on MacOS X ## On MACOS X, does not work and will be ignored
PRESERVE_EXECUTABILITY=yes PRESERVE_EXECUTABILITY=yes
## Preserve ACLS. Make sure source and target FS can manage same ACLs or you'll get loads of errors. ## Preserve ACLS. Make sure source and target FS can manage same ACLs or you'll get loads of errors.
@ -104,6 +104,9 @@ RSYNC_COMPRESS=yes
SOFT_MAX_EXEC_TIME=7200 SOFT_MAX_EXEC_TIME=7200
HARD_MAX_EXEC_TIME=10600 HARD_MAX_EXEC_TIME=10600
## Log a message every KEEP_LOGGING seconds just to know the task is still alive
KEEP_LOGGING=1801
## Minimum time (in seconds) in file monitor /daemon mode between modification detection and sync task in order to let copy operations finish. ## Minimum time (in seconds) in file monitor /daemon mode between modification detection and sync task in order to let copy operations finish.
MIN_WAIT=60 MIN_WAIT=60

View File

@ -1,143 +0,0 @@
#!/usr/bin/env bash
###### Osync - Rsync based two way sync engine with fault tolerance
###### (L) 2013-2014 by Orsiris "Ozy" de Jong (www.netpower.fr)
###### Config file rev 2611201401
## ---------- GENERAL OPTIONS
## Sync job identification
SYNC_ID="sync_test"
## Directories to synchronize. Master must be on the system Osync runs on. Slave can be either a local directory, or a remote one.
MASTER_SYNC_DIR="master"
SLAVE_SYNC_DIR="slave"
#SLAVE_SYNC_DIR="ssh://backupuser@yourhost.old:22//home/git/osync/dir2"
## If slave replica is a remote directory, you must specify a RSA key (please use full path). Please see documentation for further information.
#SSH_RSA_PRIVATE_KEY="/home/backupuser/.ssh/id_rsa"
## Create sync directories if they do not exist
CREATE_DIRS=no
## Log file location. Leaving this empty will create a logfile at /var/log/osync_version_SYNC_ID.log (or current directory if /var/log doesn't exist)
LOGFILE=""
## List of directories to exclude from sync on both sides (rsync patterns, wildcards work).
## Paths are relative to sync dirs. List elements are separated by a semicolon.
RSYNC_EXCLUDE_PATTERN=""
#RSYNC_EXCLUDE_PATTERN="tmp;archives"
## File that contains the list of directories or files to exclude from sync on both sides. Leave this empty if you don't want to use an exclusion file.
## This file has to be in the same directory as the config file
## Paths are relative to sync dirs. One element per line.
RSYNC_EXCLUDE_FROM=""
#RSYNC_EXCLUDE_FROM="exclude.list"
## List elements separator char. You may set an alternative separator char for your directories lists above.
PATH_SEPARATOR_CHAR=";"
## Generate an alert if master or slave replicas have less free space than given value in KB.
MINIMUM_SPACE=10240
## Bandwidth limit Kbytes / second. Leave 0 to disable limitation
BANDWIDTH=0
## If enabled, synchronization on remote system will be processed as superuser. See documentation for /etc/sudoers file configuration.
SUDO_EXEC=no
## Paranoia option. Don't change this unless you read the documentation.
RSYNC_EXECUTABLE=rsync
## ---------- REMOTE SYNC OPTIONS
## ssh compression should be used unless your remote connection is good enough (LAN)
SSH_COMPRESSION=yes
## Check for connectivity to remote host before launching remote sync task. Be sure the hosts responds to ping. Failing to ping will stop sync.
REMOTE_HOST_PING=no
## Check for internet access by pinging one or more 3rd party hosts before remote sync task. Leave empty if you don't want this check to be be performed. Failing to ping will stop sync.
## If you use this function, you should set more than one 3rd party host, and be sure you can ping them.
## Be aware some DNS like opendns redirect false hostnames. Also, this adds an extra execution time of a bit less than a minute.
REMOTE_3RD_PARTY_HOSTS=""
## Remote rsync executable path. Leave this empty in most cases
RSYNC_REMOTE_PATH=""
## ---------- MISC OPTIONS
## Preserve ACLS. Make sure source and target FS can manage same ACLs or you'll get loads of errors.
PRESERVE_ACL=no
## Preserve Xattr. Make sure source and target FS can manage same Xattrs or you'll get loads of errors.
PRESERVE_XATTR=no
## Transforms symlinks into referent files/dirs
COPY_SYMLINKS=no
## Treat symlinked dirs as dirs. CAUTION: This also follows symlinks outside of the replica root.
KEEP_DIRLINKS=no
## Preserve hard links. Make sure source and target FS can manage hard links or you will lose them.
PRESERVE_HARDLINKS=no
## Let RSYNC compress file transfers. Do not use this if both master and slave replicas are on local system. Also, do not use this if you already enabled SSH compression.
RSYNC_COMPRESS=yes
## Maximum execution time (in seconds) for sync process. Soft exec time only generates a warning. Hard exec time will generate a warning and stop sync process.
SOFT_MAX_EXEC_TIME=7200
HARD_MAX_EXEC_TIME=10600
## Minimum time (in seconds) in file monitor /daemon mode between modification detection and sync task in order to let copy operations finish.
MIN_WAIT=60
## ---------- BACKUP AND DELETION OPTIONS
## Enabling this option will keep a backup of a file on the target replica if it gets updated from the source replica. Backups will be made to .osync_workdir/backups
CONFLICT_BACKUP=yes
## Keep multiple backup versions of the same file. Warning, This can be very space consuming.
CONFLICT_BACKUP_MULTIPLE=no
## Osync will clean backup files after a given number of days. Setting this to 0 will disable cleaning and keep backups forever. Warning: This can be very space consuming.
CONFLICT_BACKUP_DAYS=30
## If the same file exists on both replicas, newer version will be synced. However, if both files have the same timestamp but differ, CONFILCT_PREVALANCE sets winner replica.
CONFLICT_PREVALANCE=master
## On deletion propagation to the target replica, a backup of the deleted files can be kept. Deletions will be kept in .osync_workdir/deleted
SOFT_DELETE=yes
## Osync will clean deleted files after a given number of days. Setting this to 0 will disable cleaning and keep deleted files forever. Warning: This can be very space consuming.
SOFT_DELETE_DAYS=30
## ---------- RESUME OPTIONS
## Try to resume an aborted sync task
RESUME_SYNC=yes
## Number maximum resume tries before initiating a fresh sync.
RESUME_TRY=2
## When a pidlock exists on slave replica that does not correspond to master's sync-id, force pidlock removal. Be careful with this option if you have multiple masters.
FORCE_STRANGER_LOCK_RESUME=no
## Keep partial uploads that can be resumed on next run, experimental feature
PARTIAL=no
## ---------- ALERT OPTIONS
## List of alert mails separated by spaces
DESTINATION_MAILS="your@alert.tld"
## Windows (MSYS environment) only mail options (used with sendemail.exe from Brandon Zehm)
SENDER_MAIL="alert@your.system.tld"
SMTP_SERVER=smtp.your.isp.tld
SMTP_USER=
SMTP_PASSWORD=
## ---------- EXECUTION HOOKS
## Commands can will be run before and / or after sync process (remote execution will only happen if REMOTE_SYNC is set).
LOCAL_RUN_BEFORE_CMD=""
LOCAL_RUN_AFTER_CMD=""
REMOTE_RUN_BEFORE_CMD=""
REMOTE_RUN_AFTER_CMD=""
## Max execution time of commands before they get force killed. Leave 0 if you don't wan't this to happen. Time is specified in seconds.
MAX_EXEC_TIME_PER_CMD_BEFORE=0
MAX_EXEC_TIME_PER_CMD_AFTER=0
## Stops Osync execution if one of the above commands fail
STOP_ON_CMD_ERROR=yes

View File

@ -1,143 +0,0 @@
#!/usr/bin/env bash
###### Osync - Rsync based two way sync engine with fault tolerance
###### (L) 2013-2014 by Orsiris "Ozy" de Jong (www.netpower.fr)
###### Config file rev 2611201401
## ---------- GENERAL OPTIONS
## Sync job identification
SYNC_ID="sync_test"
## Directories to synchronize. Master must be on the system Osync runs on. Slave can be either a local directory, or a remote one.
MASTER_SYNC_DIR="master"
SLAVE_SYNC_DIR="ssh://localhost//tmp/osync_tests/remote/slave"
#SLAVE_SYNC_DIR="ssh://backupuser@yourhost.old:22//home/git/osync/dir2"
## If slave replica is a remote directory, you must specify a RSA key (please use full path). Please see documentation for further information.
#SSH_RSA_PRIVATE_KEY="/home/backupuser/.ssh/id_rsa"
## Create sync directories if they do not exist
CREATE_DIRS=yes
## Log file location. Leaving this empty will create a logfile at /var/log/osync_version_SYNC_ID.log (or current directory if /var/log doesn't exist)
LOGFILE=""
## List of directories to exclude from sync on both sides (rsync patterns, wildcards work).
## Paths are relative to sync dirs. List elements are separated by a semicolon.
RSYNC_EXCLUDE_PATTERN=""
#RSYNC_EXCLUDE_PATTERN="tmp;archives"
## File that contains the list of directories or files to exclude from sync on both sides. Leave this empty if you don't want to use an exclusion file.
## This file has to be in the same directory as the config file
## Paths are relative to sync dirs. One element per line.
RSYNC_EXCLUDE_FROM=""
#RSYNC_EXCLUDE_FROM="exclude.list"
## List elements separator char. You may set an alternative separator char for your directories lists above.
PATH_SEPARATOR_CHAR=";"
## Generate an alert if master or slave replicas have less free space than given value in KB.
MINIMUM_SPACE=10240
## Bandwidth limit Kbytes / second. Leave 0 to disable limitation
BANDWIDTH=0
## If enabled, synchronization on remote system will be processed as superuser. See documentation for /etc/sudoers file configuration.
SUDO_EXEC=no
## Paranoia option. Don't change this unless you read the documentation.
RSYNC_EXECUTABLE=rsync
## ---------- REMOTE SYNC OPTIONS
## ssh compression should be used unless your remote connection is good enough (LAN)
SSH_COMPRESSION=yes
## Check for connectivity to remote host before launching remote sync task. Be sure the hosts responds to ping. Failing to ping will stop sync.
REMOTE_HOST_PING=no
## Check for internet access by pinging one or more 3rd party hosts before remote sync task. Leave empty if you don't want this check to be be performed. Failing to ping will stop sync.
## If you use this function, you should set more than one 3rd party host, and be sure you can ping them.
## Be aware some DNS like opendns redirect false hostnames. Also, this adds an extra execution time of a bit less than a minute.
REMOTE_3RD_PARTY_HOSTS=""
## Remote rsync executable path. Leave this empty in most cases
RSYNC_REMOTE_PATH=""
## ---------- MISC OPTIONS
## Preserve ACLS. Make sure source and target FS can manage same ACLs or you'll get loads of errors.
PRESERVE_ACL=no
## Preserve Xattr. Make sure source and target FS can manage same Xattrs or you'll get loads of errors.
PRESERVE_XATTR=no
## Transforms symlinks into referent files/dirs
COPY_SYMLINKS=no
## Treat symlinked dirs as dirs. CAUTION: This also follows symlinks outside of the replica root.
KEEP_DIRLINKS=no
## Preserve hard links. Make sure source and target FS can manage hard links or you will lose them.
PRESERVE_HARDLINKS=no
## Let RSYNC compress file transfers. Do not use this if both master and slave replicas are on local system. Also, do not use this if you already enabled SSH compression.
RSYNC_COMPRESS=yes
## Maximum execution time (in seconds) for sync process. Soft exec time only generates a warning. Hard exec time will generate a warning and stop sync process.
SOFT_MAX_EXEC_TIME=7200
HARD_MAX_EXEC_TIME=10600
## Minimum time (in seconds) in file monitor /daemon mode between modification detection and sync task in order to let copy operations finish.
MIN_WAIT=60
## ---------- BACKUP AND DELETION OPTIONS
## Enabling this option will keep a backup of a file on the target replica if it gets updated from the source replica. Backups will be made to .osync_workdir/backups
CONFLICT_BACKUP=yes
## Keep multiple backup versions of the same file. Warning, This can be very space consuming.
CONFLICT_BACKUP_MULTIPLE=no
## Osync will clean backup files after a given number of days. Setting this to 0 will disable cleaning and keep backups forever. Warning: This can be very space consuming.
CONFLICT_BACKUP_DAYS=30
## If the same file exists on both replicas, newer version will be synced. However, if both files have the same timestamp but differ, CONFILCT_PREVALANCE sets winner replica.
CONFLICT_PREVALANCE=master
## On deletion propagation to the target replica, a backup of the deleted files can be kept. Deletions will be kept in .osync_workdir/deleted
SOFT_DELETE=yes
## Osync will clean deleted files after a given number of days. Setting this to 0 will disable cleaning and keep deleted files forever. Warning: This can be very space consuming.
SOFT_DELETE_DAYS=30
## ---------- RESUME OPTIONS
## Try to resume an aborted sync task
RESUME_SYNC=yes
## Number maximum resume tries before initiating a fresh sync.
RESUME_TRY=2
## When a pidlock exists on slave replica that does not correspond to master's sync-id, force pidlock removal. Be careful with this option if you have multiple masters.
FORCE_STRANGER_LOCK_RESUME=no
## Keep partial uploads that can be resumed on next run, experimental feature
PARTIAL=no
## ---------- ALERT OPTIONS
## List of alert mails separated by spaces
DESTINATION_MAILS="your@alert.tld"
## Windows (MSYS environment) only mail options (used with sendemail.exe from Brandon Zehm)
SENDER_MAIL="alert@your.system.tld"
SMTP_SERVER=smtp.your.isp.tld
SMTP_USER=
SMTP_PASSWORD=
## ---------- EXECUTION HOOKS
## Commands can will be run before and / or after sync process (remote execution will only happen if REMOTE_SYNC is set).
LOCAL_RUN_BEFORE_CMD=""
LOCAL_RUN_AFTER_CMD=""
REMOTE_RUN_BEFORE_CMD=""
REMOTE_RUN_AFTER_CMD=""
## Max execution time of commands before they get force killed. Leave 0 if you don't wan't this to happen. Time is specified in seconds.
MAX_EXEC_TIME_PER_CMD_BEFORE=0
MAX_EXEC_TIME_PER_CMD_AFTER=0
## Stops Osync execution if one of the above commands fail
STOP_ON_CMD_ERROR=yes