Thursday, 21 November
2013
Thu, 21 Nov
'13
10:57 a.m.
add routine that allows waiting for any command to return
specified exit code
Signed-off-by: Hubert Kario <hkario(a)redhat.com>
---
src/synchronisation.sh | 197 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 196 insertions(+), 1 deletion(-)
diff --git a/src/synchronisation.sh b/src/synchronisation.sh
index 2e25dd4..b957b4d 100644
--- a/src/synchronisation.sh
+++ b/src/synchronisation.sh
@@ -57,6 +57,148 @@ __INTERNAL_killtree() {
return ${_ret:-0}
}
+# Since all "wait for something to happen" utilities are basically the same,
+# use a generic routine that can do all their work
+__INTERNAL_wait_for_cmd() {
+
+ # don't wait more than this many seconds
+ local timeout=120
+ # delay between command invocations
+ local delay=1
+ # abort if this process terminates
+ local proc_pid=1
+ # command to run
+ local cmd
+ # maximum number of command invocations
+ local max_invoc=""
+ # expected return code of command
+ local exp_retval=0
+ # name of routine to return errors for
+ local routine_name="$1"
+ shift 1
+
+ # that is the GNU extended getopt syntax!
+ local TEMP=$(getopt -o t:p:m:d:r: -n '$routine_name' -- "$@")
+ if [[ $? != 0 ]] ; then
+ rlLogError "$routine_name: Can't parse command options,
terminating..."
+ return 127
+ fi
+
+ eval set -- "$TEMP"
+
+ while true ; do
+ case "$1" in
+ -t) timeout="$2"; shift 2
+ ;;
+ -p) proc_pid="$2"; shift 2
+ ;;
+ -m) max_invoc="$2"; shift 2
+ ;;
+ -d) delay="$2"; shift 2
+ ;;
+ -r) exp_retval="$2"; shift 2
+ ;;
+ --) shift 1
+ break
+ ;;
+ *) rlLogError "$routine_name: unrecognized option"
+ return 127
+ ;;
+ esac
+ done
+ cmd="$1"
+
+ if [[ $routine_name == "rlWaitForCmd" ]]; then
+ rlLogInfo "$routine_name: waiting for \`$cmd' to return $exp_retval in
$timeout seconds"
+ fi
+
+ # the case statement is a portable way to check if variable contains only
+ # digits (regexps are not available in old, RHEL3-era, bash)
+ case "$timeout" in
+ ''|*[!0-9]*) rlLogError "${routine_name}: Invalid timeout
provided"
+ return 127
+ ;;
+ esac
+ case "$proc_pid" in
+ ''|*[!0-9]*) rlLogError "${routine_name}: Invalid PID
provided"
+ return 127
+ ;;
+ esac
+ if [[ -n "$max_invoc" ]]; then
+ case "$max_invoc" in
+ ''|*[!0-9]*) rlLogError "${routine_name}: Invalid maximum number
of invocations provided"
+ return 127
+ ;;
+ esac
+ fi
+ # delay can be fractional, so "." is OK
+ case "$delay" in
+ ''|*[!0-9.]*) rlLogError "${routine_name}: Invalid delay
specified"
+ return 127
+ ;;
+ esac
+ case "$exp_retval" in
+ ''|*[!0-9]*) rlLogError "${routine_name}: Invalid expected command
return value provided"
+ return 127
+ ;;
+ esac
+
+ # we use two child processes to get the timeout and process execution
+ # one (command_pid) runs the command until it returns expected return value
+ # the other is just a timout (watcher)
+
+ # run command in loop
+ ( local i=0
+ while [[ -n $max_invoc && $i -lt $max_invoc ]] || [[ ! -n $max_invoc ]]; do
+ eval $cmd
+ if [[ $? -eq $exp_retval ]]; then
+ exit 0;
+ else
+ if [[ ! -e "/proc/$proc_pid" ]]; then
+ exit 1;
+ fi
+ sleep $delay
+ fi
+ i=$((i+1))
+ done
+ exit 2) &
+ local command_pid=$!
+
+ # kill command running in background if the timout has elapsed
+ ( sleep $timeout && __INTERNAL_killtree $command_pid SIGKILL) 2>/dev/null
&
+ local watcher=$!
+
+ wait $command_pid 2> /dev/null
+ local ret=$?
+ if [[ $ret -eq 0 ]]; then
+ __INTERNAL_killtree $watcher SIGKILL 2>/dev/null
+ wait $watcher 2> /dev/null
+ rlLogInfo "${routine_name}: Wait successful!"
+ return 0
+ else
+ case $ret in
+ 1)
+ __INTERNAL_killtree $watcher SIGKILL 2>/dev/null
+ wait $watcher 2> /dev/null
+ rlLogWarning "${routine_name}: specified PID was terminated!"
+ ;;
+ 2)
+ __INTERNAL_killtree $watcher SIGKILL 2>/dev/null
+ wait $watcher 2> /dev/null
+ rlLogWarning "${routine_name}: Max number of test command
invocations reached!"
+ ;;
+ 143|137)
+ rlLogWarning "${routine_name}: Timeout reached"
+ ;;
+ *)
+ __INTERNAL_killtree $watcher SIGKILL 2>/dev/null
+ wait $watcher 2> /dev/null
+ rlLogError "${routine_name}: Unknown termination cause! Return code:
$ret"
+ esac
+ return 1
+ fi
+}
+
: <<'=cut'
=pod
@@ -77,13 +219,66 @@ if those test fail, use their return codes and rlFail().
=cut
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-# rlWaitForSocket
+# rlWaitForCmd
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
: <<'=cut'
=pod
=head2 Process Synchronisation
+=head3 rlWaitForCmd
+
+Pauses script execution until command exit status is the expeced value.
+Logs a WARNING and returns 1 if the command didn't exit successfully
+before timeout elapsed or a maximum number of invocations has been
+reached.
+
+ rlWaitForCmd command [-p PID] [-t time] [-m count] [-d delay] [-r retval]
+
+=over
+
+=item command
+
+Command that will be executed until its return code is equal 0 or value
+speciefied as option to `-r'.
+
+=item -t time
+
+Timeout in seconds, default=120. If the command doesn't return 0
+before time elapses, the command will be killed.
+
+=item -p PID
+
+PID of the process to check before running command. If the process
+exits before the socket is opened, the command will log a WARNING.
+
+=item -m count
+
+Maximum number of `command' executions before continuing anyway. Default is
+infite. Returns 1 if the maximum was reached.
+
+=item -d delay
+
+Delay between `command' invocations. Default 1.
+
+=item -r retval
+
+Expected return value of command. Default 0.
+
+=back
+
+=cut
+rlWaitForCmd() {
+ __INTERNAL_wait_for_cmd rlWaitForCmd "$@"
+}
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# rlWaitForSocket
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+: <<'=cut'
+=pod
+
=head3 rlWaitForSocket
Pauses script execution until socket starts listening.
--
1.8.3.1