# shellcheck shell=bash disable=SC2148 disable=SC2034
#==============================================================================
# Copyright and license info is available in the LICENSE file included with
# the Server Deployment Package (SDP), and also available online:
# https://swarm.workshop.perforce.com/projects/perforce-software-sdp/view/main/LICENSE
#------------------------------------------------------------------------------
#==============================================================================
# Library Functions.
#------------------------------------------------------------------------------
# Function: run
#
# Short: Run a command with optional description, honoring global $NoOp
# settings.
#
# Input:
# $1 - cmd. The command to run. The command is displayed before executing.
# $2 - desc. Optional text description displayed before the command runs.
# A '|' character splits the description across multiple lines. Each
# line may be prefixed with 'N:' (single digit) to display that line only
# when $Debug >= N. Without an 'N:' prefix the line is always displayed.
# Examples: "Starting backup."
# "1:Verbose detail shown only at debug level 1+."
# "First line.|Second line."
# "0:Always shown.|2:Debug-only detail."
# $3 - honorNoOpFlag. Pass 1 to honor the $NoOp setting (display but don't
# run the command when $NoOp is set). Pass 0 to always run.
# Default: 1.
# $4 - alwaysShowOutputFlag. Pass 1 to show command output regardless of
# $Debug level. Otherwise output is shown only when $Debug >= 1.
# Default: 0.
# $5 - grepString. If set, changes the return code: returns 0 if the string
# is found in the output, 1 otherwise. Supports egrep patterns.
#
# Sets library output variables CMDLAST (last command run) and CMDEXITCODE
# (its exit code) on each call.
#------------------------------------------------------------------------------
function run () {
dbg "CALL: run ($*)"
local cmd=${1:-}
local desc=${2:-}
local -i honorNoOpFlag=${3:-1}
local -i alwaysShowOutputFlag=${4:-0}
local grepString=${5:-}
local cmdScript=
local cmdOut=
local -i grepExit
CMDLAST="$cmd"
CMDEXITCODE=0
if [[ -n "$desc" ]]; then
echo "$desc" | tr '|' '\n' | while read -r text; do
if [[ $text =~ ^[0-9]+: ]]; then
local descVerbosity=${text%%:*}
text=${text#"$descVerbosity:"}
[[ "$Debug" -ge "$descVerbosity" ]] && msg "$text"
else
msg "$text"
fi
done
fi
if [[ "$honorNoOpFlag" -eq 1 && "$NoOp" -eq 1 ]]; then
msg "NO-OP: Would run: \"$cmd\"\n"
else
msg "Running: \"$cmd\"."
cmdScript="$(mktemp -t run.XXXXXX).cmd.sh"
cmdOut="${cmdScript%.cmd.sh}.out"
echo -e "#!/bin/bash\n$cmd\n" > "$cmdScript"
chmod +x "$cmdScript"
"$cmdScript" > "$cmdOut" 2>&1
CMDEXITCODE=$?
if [[ -n "$grepString" ]]; then
# shellcheck disable=SC2196
egrep "$grepString" "$cmdOut" > /dev/null 2>&1
grepExit=$?
CMDEXITCODE="$grepExit"
fi
if [[ "$alwaysShowOutputFlag" -eq 1 ]]; then
cat "$cmdOut"
else
[[ "$Debug" -ge 1 ]] && cat "$cmdOut"
fi
# Be clean and tidy.
/bin/rm -f "$cmdScript" "$cmdOut"
# If a grep was requested, return the exit code from the egrep,
# otherwise return the exit code of the command executed. In
# any case, $CMDEXITCODE contains the exit code of the command.
if [[ -n "$grepString" ]]; then
return $grepExit
else
return $CMDEXITCODE
fi
fi
return 0
}
#------------------------------------------------------------------------------
# Function: rrun
#
# Short: Run a command on a remote host, honoring $Debug and $NoOp settings.
#
# Input:
# $1 - host. The remote host to run the command on.
# $2 - cmd. The command to run. The command is displayed before executing.
# $3 - desc. Optional text description displayed before the command runs.
# Supports the same '|' line-splitting and 'N:' verbosity-prefix syntax
# as the desc parameter of run().
# $4 - honorNoOpFlag. Pass 1 to honor the $NoOp setting (display but don't
# run the command when $NoOp is set). Pass 0 to always run.
# Default: 1.
# $5 - alwaysShowOutputFlag. Pass 1 to show command output regardless of
# $Debug level. Otherwise output is shown only when $Debug >= 1.
# Default: 0.
# $6 - grepString. If set, changes the return code: returns 0 if the string
# is found in the output, 1 otherwise. Supports egrep patterns.
#
# Sets library output variables RCMDLAST (last command run) and RCMDEXITCODE
# (its exit code) on each call.
#------------------------------------------------------------------------------
function rrun () {
dbg "CALL: rrun ($*)"
local host=${1:-Unset}
local cmd=${2:-Unset}
local desc=${3:-}
local -i honorNoOpFlag=${4:-1}
local -i alwaysShowOutputFlag=${5:-0}
local grepString=${6:-}
local rCmdScript=
local rCmdOut=
local -i grepExit
RCMDLAST="$cmd"
RCMDEXITCODE=0
if [[ -n "$desc" ]]; then
echo "$desc" | tr '|' '\n' | while read -r text; do
if [[ $text =~ ^[0-9]+: ]]; then
local descVerbosity=${text%%:*}
text=${text#"$descVerbosity:"}
[[ "$Debug" -ge "$descVerbosity" ]] && msg "$text"
else
msg "$text"
fi
done
fi
if [[ "$honorNoOpFlag" -eq 1 && "$NoOp" -eq 1 ]]; then
msg "NO-OP: Would run: \"$cmd\" on host $host.\n"
else
msg "Running: \"$cmd\" on host $host."
rCmdScript="$(mktemp -t rrun.XXXXXX).cmd.sh"
rCmdOut="${rCmdScript%.cmd.sh}.out"
# Compute the path the script will have on the remote host after scp.
local remoteScript="${P4TMP:-/tmp}/$(basename "$rCmdScript")"
echo -e "#!/bin/bash\n$cmd\n" > "$rCmdScript"
chmod +wx "$rCmdScript"
if ! scp -pq "$rCmdScript" "$host:${P4TMP:-/tmp}/."; then
RCMDEXITCODE=-1
errmsg "rrun(): Failed to copy temp command script to $host."
return 1
fi
ssh -q -n "$host" "$remoteScript" > "$rCmdOut" 2>&1
RCMDEXITCODE=$?
# Clean up the remote temp file.
ssh -q -n "$host" "rm -f '$remoteScript'" 2>/dev/null
if [[ -n "$grepString" ]]; then
# shellcheck disable=SC2196
egrep "$grepString" "$rCmdOut" > /dev/null 2>&1
grepExit=$?
RCMDEXITCODE="$grepExit"
fi
if [[ $alwaysShowOutputFlag -eq 1 ]]; then
cat "$rCmdOut"
else
[[ "$Debug" -ge 1 ]] && cat "$rCmdOut"
fi
# Be clean and tidy.
/bin/rm -f "$rCmdScript" "$rCmdOut"
# If a grep was requested, return the exit code from the egrep,
# otherwise return the exit code of the command remotely executed.
# In any case, $RCMDEXITCODE contains the exit code of the command.
if [[ -n "$grepString" ]]; then
return $grepExit
else
return $RCMDEXITCODE
fi
fi
return 0
}