add routine that allows waiting for any command to return specified exit code
Signed-off-by: Hubert Kario hkario@redhat.com --- src/synchronisation.sh | 185 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 184 insertions(+), 1 deletion(-)
diff --git a/src/synchronisation.sh b/src/synchronisation.sh index e782481..2885058 100644 --- a/src/synchronisation.sh +++ b/src/synchronisation.sh @@ -57,6 +57,136 @@ __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 1) & + 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 + if [[ $ret -eq 1 ]]; then + __INTERNAL_killtree $watcher SIGKILL 2>/dev/null + wait $watcher 2> /dev/null + rlLogWarning "${routine_name}: PID terminated!" + else + rlLogWarning "${routine_name}: Timeout elapsed" + fi + return 1 + fi +} + : <<'=cut' =pod
@@ -74,13 +204,66 @@ of applications. =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] [-n 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. + +=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.