# 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 }