# shellcheck 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://workshop.perforce.com/view/p4-sdp/main/LICENSE
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Version ID Block. Relies on +k filetype modifier.
# VersionID='$Id: //p4-sdp/r26.1.0.BETA/Server/Unix/p4/common/lib/libcore.sh#1 $ $Change: 33444 $'
#==============================================================================
# Library Functions.
#------------------------------------------------------------------------------
# Function: bail
#
# Input:
# $1 - error message, displayed to stderr.
# $2 - Return code, default is 1
#------------------------------------------------------------------------------
function bail {
declare msg=${1:-Unknown Error}
declare -i rc=${2:-1}
echo -e "\n$THISSCRIPT (line: ${BASH_LINENO[0]}): FATAL: $msg\n\n" >&2
exit "$rc"
}
#------------------------------------------------------------------------------
# Function: initlog
#------------------------------------------------------------------------------
function initlog
{
[[ $VERBOSITY -gt 1 ]] && \
echo -e "${H}\n$THISSCRIPT started at $(date) as pid $$:\nInitial Command Line:\n$CMDLINE\nLog file is: ${P4U_LOG}\n\n"
}
#------------------------------------------------------------------------------
# Function: stoplog
function stoplog
{
sleep 1
[[ $VERBOSITY -gt 1 ]] && \
echo -e "\n$THISSCRIPT stopped at $(date).\n\nLog file is: ${P4U_LOG}${H}\n"
}
#------------------------------------------------------------------------------
# Function: errmsg
#
# Description: Display error message on stderr, regardless of $VERBOSITY value.
#
# Input:
# $1 - error message
#------------------------------------------------------------------------------
function errmsg {
echo -e "$THISSCRIPT: ERROR: ${1:-Unknown Error}\n" >&2
}
#------------------------------------------------------------------------------
# Function: warnmsg
#
# Description: Display a warning message, but only if $VERBOSITY > 1.
#
# Input:
# $1 - warning message
#------------------------------------------------------------------------------
function warnmsg {
[[ $VERBOSITY -gt 1 ]] && echo -e "$THISSCRIPT: WARNING: $1\n"
}
#------------------------------------------------------------------------------
# Function: qmsg
#
# Description: Display a quiet message. Uses the VERBOSITY env var, and
# displays messages only if $VERBOSITY > 1.
#
# Input:
# $1 - message
#------------------------------------------------------------------------------
function qmsg {
[[ $VERBOSITY -gt 1 ]] && echo -e "$*\n"
}
#------------------------------------------------------------------------------
# Function: msg
#
# Description: Display a normal message. Uses the VERBOSITY env var, and
# displays messages only if $VERBOSITY > 2.
#
# Input:
# $1 - message
#------------------------------------------------------------------------------
function msg {
[[ $VERBOSITY -gt 2 ]] && echo -e "$*\n"
}
#------------------------------------------------------------------------------
# Function: vmsg
#
# Description: Display a verbose message. Uses the VERBOSITY env var, and
# displays messages only if $VERBOSITY > default 3.
#
# Input:
# $1 - message
#------------------------------------------------------------------------------
function vmsg {
[[ $VERBOSITY -gt 3 ]] && echo -e "$*\n"
}
#------------------------------------------------------------------------------
# Function: vvmsg
#
# Description: Display a very verbose message. Uses the VERBOSITY env var, and
# displays messages only if $VERBOSITY > default 4.
#
# Input:
# $1 - message
#------------------------------------------------------------------------------
function vvmsg {
[[ $VERBOSITY -gt 4 ]] && echo -e "$*\n"
}
#------------------------------------------------------------------------------
# Function: cleanTrash
#
# Usage: During operation of your program, append the $GARBAGE variable
# with garbage file or directory names you want to have cleaned up
# automatically on exit, e.g.:
# GARBAGE+=" $MyTmpFile"
#
# The $P4U_TMPDIR is a guaranteed unique directory created by 'mktemp -d',
# and is automatically added to $GARBAGE. So creating temp files in
# that directory will get them cleaned up on exit without needing to
# add them to $GARBAGE explicitly, e.g.:
#
# MyTempFile=$P4U_TMPDIR/gen_file.txt
#
# Specify absolute paths for garbage files, or else ensure that the paths
# specified will be valid when the 'rm -rf' command is run.
#
# To specify remote garbage to clean up, append the $RGARBAGE variable,
# e.g.
# RGARBAGE+=" $other_host:$tmpFile"
#
# Input:
# $1 - Optionally specify 'verbose'; otherwise this routine does its
# work silently.
#------------------------------------------------------------------------------
function cleanTrash {
declare vmode=${1:-silent}
declare v=$VERBOSITY
[[ "$vmode" == "silent" ]] && export VERBOSITY=1
if [[ -n "$RGARBAGE" ]]; then
for rFile in $RGARBAGE; do
rHost=${rFile%%:*}
rFile=${rFile##*:}
rrun "$rHost" "/bin/rm -f $rFile" "Cleaning up remote garbage file [$rHost:$rFile].\n"
done
fi
if [[ -n "$GARBAGE" ]]; then
vvmsg "Cleaning up garbage files."
# shellcheck disable=SC2086
/bin/rm -rf $GARBAGE
fi
export VERBOSITY=$v
}
#------------------------------------------------------------------------------
# Function: run
#
# Short: Run a command with optional description, honoring $VERBOSITY
# and $NO_OP settings. Simpler than runCmd().
#
# Input:
# $1 - cmd. The command to run. The command is displayed first
# if $VERBOSITY > 3.
# $2 - desc. Text description of command to run.
# Optionally, the text string can be prefixed with 'N:', where N is
# a one-digit integer, the minimum verbosity at which the description is to
# be displayed.
# Optionally, a series of text strings can be provided, delimited by '|',
# allowing multiple descriptions to be provided, each with a different
# minimum verbosity setting.
# This parameter is optional. If the N: prefix is omitted, it is
# equivalent to "3:". Sample values: "5:Cool command here:",
# and "3:Regular verbosity message|4:Higher verbosity message."
# and "This is a very|long description."
# $3 - honorNoOpFlag. Pass in 1 to mean "Yes, honor the $NO_OP setting
# and display (but don't run) commands if $NO_OP is set." Otherwise
# $NO_OP is ignored, and the command is always run.
# This parameter is optional; the default value is 1.
# $4 - alwaysShowOutputFlag. If set to 1, show output regardless of $VERBOSITY
# value. Otherwise, only show output if VERBOSITY > 4.
# This parameter is optional; the default value is 0.
# $5 - grepString. If set, this changes the exit code behavior.
# If the specified string exists in the output, a 0 is returned, else 1.
# Strings suitable for use with 'egrep' are allowed.
#
# Description:
# Display an optional description of a command, and then run the
# command. This is affected by $NO_OP and $VERBOSITY. If $NO_OP is
# set, the command is shown, but not run, provided $honorNoOpFlag is 1.
# The description is not shown if $VERBOSITY < 3.
#
# The variables CMDLAST and CMDEXITCODE are set each time runCmd is called.
# CMDLAST contains the last command run. CMDEXITCODE contains its exit code.
#
# Output is shown if either $AlwaysShowOutputFlag is 1 or $VERBOSITY >= 4.
#------------------------------------------------------------------------------
function run () {
vvmsg "CALL: run ($*)"
declare cmd=${1:-}
declare desc=${2:-}
declare -i honorNoOpFlag=${3:-1}
declare -i alwaysShowOutputFlag=${4:-0}
declare grepString=${5:-}
declare cmdScript=$P4U_TMPDIR/cmd.sh
declare cmdOut=$P4U_TMPDIR/script.out
declare -i grepExit
CMDLAST=$cmd
CMDEXITCODE=0
if [[ -n "$desc" ]]; then
# Descriptions intended to be multi-line contain a '|' char.
# Each entry may contain an optional 'N:' prefix where N is the
# verbosity level at or above which the message is displayed.
echo "$desc" | tr '|' '\n' | while read -r text; do
if [[ $text =~ ^[0-9]+: ]]; then
descVerbosity=${text%%:*}
text=${text#$descVerbosity:}
if [[ $VERBOSITY -ge $descVerbosity ]]; then
echo -e "$text"
fi
else
msg "$desc"
fi
done
fi
if [[ $honorNoOpFlag -eq 1 && $NO_OP -eq 1 ]]; then
vmsg "NO-OP: Would run: \"$cmd\"\n"
else
vmsg "Running: \"$cmd\"."
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
[[ $VERBOSITY -gt 3 ]] && 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 with on a remote host optional description, honoring
# $VERBOSITY and $NO_OP settings. Simpler than runRemoteCmd().
#
# Input:
# $1 - host. The remote host to run the command on.
# $2 - cmd. The command to run. The command is displayed first
# if $VERBOSITY > 3.
# $3 - desc. Text description of command to run.
# Optionally, the text string can be prefixed with 'N:', where N is
# a one-digit integer, the minimum verbosity at which the description is to
# be displayed.
# Optionally, a series of text strings can be provided, delimited by '|',
# allowing multiple descriptions to be provided, each with a different
# minimum verbosity setting.
# This parameter is optional. If the N: prefix is omitted, it is
# equivalent to "3:". Sample values: "5:Cool command here:"
# $4 - honorNoOpFlag. Pass in 1 to mean "Yes, honor the $NO_OP setting
# and display (but don't run) commands if $NO_OP is set." Otherwise
# $NO_OP is ignored, and the command is always run.
# This parameter is optional; the default value is 1.
# $5 - alwaysShowOutputFlag. If set to 1, show output regardless of $VERBOSITY
# value. Otherwise, only show output if VERBOSITY > 4.
# This parameter is optional; the default value is 0.
# $6 - grepString. If set, this changes the exit code behavior.
# If the specified string exists in the output, a 0 is returned, else 1.
# Strings suitable for use with 'egrep' are allowed.
#
# Description:
# Display an optional description of a command, and then run the
# command. This is affected by $NO_OP and $VERBOSITY. If $NO_OP is
# set, the command is shown, but not run, provided $honorNoOpFlag is 1.
# The description is not shown if $VERBOSITY < 3.
#
# The variables RCMDLAST and RCMDEXITCODE are set each time runCmd is called.
# RCMDLAST contains the last command run. RCMDEXITCODE contains its exit code.
#
# Output is shown if either $AlwaysShowOutputFlag is 1 or $VERBOSITY >= 4.
#------------------------------------------------------------------------------
function rrun () {
vvmsg "CALL: rrun ($*)"
declare host=${1:-Unset}
declare cmd=${2:-Unset}
declare desc=${3:-}
declare -i honorNoOpFlag=${4:-1}
declare -i alwaysShowOutputFlag=${5:-0}
declare grepString=${6:-}
declare rCmdScript="${P4TMP:-/tmp}/rcmd.$$.${RANDOM}${RANDOM}.sh"
declare rCmdOut=$P4U_TMPDIR/rcmd.out
declare -i grepExit
RCMDLAST="$cmd"
RCMDEXITCODE=0
if [[ -n "$desc" ]]; then
echo "$desc" | tr '|' '\n' | while read -r text; do
if [[ $text =~ ^[0-9]+: ]]; then
descVerbosity=${text%%:*}
text=${text#$descVerbosity:}
if [[ $VERBOSITY -ge $descVerbosity ]]; then
echo -e "$text"
fi
else
msg "$desc"
fi
done
fi
if [[ $honorNoOpFlag -eq 1 && $NO_OP -eq 1 ]]; then
vmsg "NO-OP: Would run: \"$cmd\" on host $host.\n"
else
vmsg "Running: \"$cmd\" on host $host."
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" "$rCmdScript" > "$rCmdOut" 2>&1
RCMDEXITCODE=$?
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
[[ $VERBOSITY -gt 3 ]] && 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
}
#------------------------------------------------------------------------------
# Function: runCmd
#
# Short: Run a command with with optional description, honoring $VERBOSITY
# and $NO_OP settings.
#
# Input:
# $1 - cmd. The command to run. The command is displayed first
# if $VERBOSITY > 3.
# $2 - textDesc. Text description of command to run. This is displayed
# if $VERBOSITY > 2.
# This parameter is optional.
# $3 - honorNoOpFlag. Pass in 1 to mean "Yes, honor the $NO_OP setting
# and display (but don't run) commands if $NO_OP is set." Otherwise
# $NO_OP is ignored, and the command is always run.
# This parameter is optional; the default value is 1.
# $4 - ShowOutputFlag. If set to 1, show output regardless of $VERBOSITY
# value. Otherwise, only show output if VERBOSITY > 4.
# This parameter is optional; the default value is 1.
# $5 - CaptureOutputFlag. If set to 1, attempt to capture the output,
# otherwise, do not. The default is 1. This should be set to 0
# in cases where commands generate large amounts of output that do
# not require further processing or parsing. Regardless of the
# value specified, the output of commands containing redirection
# operators '<' and/or '>' are not captured.
#
# Description:
# Display an optional description of a command, and then run the
# command. This is affected by $NO_OP and $VERBOSITY. If $NO_OP is
# set, the command is shown, but not run, provided $honorNoOpFlag is 1.
# The description is not shown if $VERBOSITY < 3.
#
# The variables CMDLAST, CMDEXITCODE, and CMDOUTPUT are set each time
# runCmd is called, containing the last command run, its exit code and
# output in string and file forms.
#
# If the command contains redirect operators '>' or '<', it is executed
# by being written as a generated temporary bash script. The CMDOUTPUT
# value is set to "Output Not Captured." in that case, or if
# CaptureOutputFlag is set to 0.
#
# Usage Example:
# Run the 'ls' command on $path, and bail if the return status of the
# executed command is non-zero, and run it even if $NO_OP is set:
#
# runCmd "ls $path" "Contents of [$path]:" 0 || bail "Couldn't ls [$path]."
#
#------------------------------------------------------------------------------
function runCmd {
declare cmd=$1
declare textDesc=${2:-""}
declare -i honorNoOpFlag=${3:-1}
declare -i showOutputFlag=${4:-1}
declare -i captureOutputFlag=${5:-1}
declare tmpScript=
# shellcheck disable=SC2034
CMDLAST=$cmd
CMDEXITCODE=0
CMDOUTPUT=""
[[ -n "$textDesc" ]] && msg "$textDesc"
if [[ $honorNoOpFlag -eq 1 && $NO_OP -eq 1 ]]; then
vmsg "NO-OP: Would execute: \"$cmd\"\n"
else
vmsg "Executing: \"$cmd\"."
# Execute the command, and immediately capture the return status.
# Capture output if $captureOutputFlag is 1 and there are no redirect
# operators.
if [[ $captureOutputFlag -eq 0 || $cmd == *"<"* || $cmd == *">"* ]]; then
tmpScript="$P4U_TMPDIR/cmd.sh"
echo -e "#!/bin/bash\n$cmd\n" > "$tmpScript"
chmod +x "$tmpScript"
$tmpScript
CMDEXITCODE=$?
CMDOUTPUT="Output Not Captured."
/bin/rm -f "$tmpScript"
else
CMDOUTPUT=$($cmd 2>&1)
CMDEXITCODE=$?
fi
[[ $showOutputFlag -eq 1 ]] && msg "\n$CMDOUTPUT\nEXIT_CODE: $CMDEXITCODE\n"
return $CMDEXITCODE
fi
return 0
}
#------------------------------------------------------------------------------
# Function: runRemoteCmd
#
# Short: Run a remote command on another host, with functionality otherwise
# similar to runCmd(). Execute by generating a temporary remote /bin/bash
# script to insulate against shell compatibility issues, so things work as
# expected even if the default login shell is a foreign shell like tcsh.
#
# Input:
# $1 - host. Specify the remote hostname. Note that SSH keys must be
# configured to allow remote execution without a password for
# automation of remote processing.
# $2 - cmd. The command to run. The command is displayed first
# if $VERBOSITY > 3.
# $3 - textDesc. Text description of command to run. This is displayed
# if $VERBOSITY > 2.
# This parameter is optional.
# $4 - honorNoOpFlag. Pass in 1 to mean "Yes, honor the $NO_OP setting
# and display (but don't run) commands if $NO_OP is set." Otherwise
# $NO_OP is ignored, and the command is always run.
# This parameter is optional; the default value is 1.
# $5 - ShowOutputFlag. If set to 1, show output regardless of $VERBOSITY
# value. Otherwise, only show output if VERBOSITY > 4.
# This parameter is optional; the default value is 1.
# $6 - CaptureOutputFlag. If set to 1, attempt to capture the output,
# otherwise, do not. The default is 1. This should be set to 0
# in cases where commands generate large amounts of output that do
# not require further processing or parsing.
#
# Description:
# Display an optional description of a command, and then run the
# command on a remote host. This is affected by $NO_OP and $VERBOSITY.
# If $NO_OP is set, the command is shown, but not run, provided
# $honorNoOpFlag is 1. The description is not shown if $VERBOSITY <3.
#
# The variables RCMDLAST, RCMDEXITCODE, RCMDOUTPUT are set each time this
# is run, containing the last command run, its exit code and output.
#
# The RCMDOUTPUT value is set to "Output Not Captured." if the
# CaptureOutputFlag is set to 0.
#
# To insulate against shell incompatibilities and the default login shell
# possibly being something other than bash, a bash script is generated
# and then executed on the remote host.
#
# Usage Example:
# Run the 'ls' command on $path, and bail if the return status of the
# executed command is non-zero, and run it even if $NO_OP is set:
#
# runRemoteCmd scm02 "ls $path" "In [$path]:" 0 || bail "Couldn't ls [$path]."
#
#------------------------------------------------------------------------------
function runRemoteCmd {
declare host=$1
declare rCmd=$2
declare textDesc=${3:-""}
declare -i honorNoOpFlag=${4:-1}
declare -i showOutputFlag=${5:-1}
declare -i captureOutputFlag=${6:-1}
declare remoteScript="${P4TMP:-/tmp}/runRemoteCmd.$$.${RANDOM}${RANDOM}.sh"
declare outputFile="${P4TMP:-/tmp}/remoteCmdOutput.$$.${RANDOM}${RANDOM}.out"
declare ec=""
# shellcheck disable=SC2034
RCMDLAST=${rCmd}
RCMDEXITCODE=0
RCMDOUTPUT=""
[[ -n "$textDesc" ]] && msg "$textDesc"
if [[ $honorNoOpFlag -eq 1 && $NO_OP -eq 1 ]]; then
vmsg "NO-OP: Would execute: \"$rCmd\"\n"
else
vmsg "Executing: \"$rCmd\" on remote host ${host}."
# Generate a script with the command to execute on the remote host.
echo -e "#!/bin/bash\n$rCmd\necho REMOTE_EXIT_CODE: \$?\n" > "$remoteScript"
chmod +wx "$remoteScript"
vvmsg "Script to execute [$remoteScript]:\n$(cat "$remoteScript")\n"
runCmd "scp -pq $remoteScript ${host}:${remoteScript}" || \
bail "Failed to copy script to remote host!"
( ssh -q -n "$host" "$remoteScript" ) > "$outputFile" 2>&1 &
# shellcheck disable=SC2078
while [[ /bin/true ]]; do
sleep 2
#vvmsg "Checking remote process log [$outputFile]."
if ec=$(grep -a "REMOTE_EXIT_CODE:" "$outputFile"); then
ec=${ec##*REMOTE_EXIT_CODE: }
ec=${ec%% *}
RCMDEXITCODE=$ec
break
fi
done
if [[ $showOutputFlag -eq 1 ]]; then
cat "$outputFile"
fi
if [[ $captureOutputFlag -eq 1 ]]; then
RCMDOUTPUT="$(cat "$outputFile")"
else
# shellcheck disable=SC2034
RCMDOUTPUT="Output Not Captured."
fi
ssh -q "$host" /bin/rm -f "$remoteScript"
[[ -e "$outputFile" ]] && rm -f "$outputFile"
fi
return 0
}
#------------------------------------------------------------------------------
# Function: usageError (usage error message)
#
# $1 - message
#------------------------------------------------------------------------------
function usageError {
echo -e "$THISSCRIPT: ERROR: $1\n"
usage -h
}
#------------------------------------------------------------------------------
# Append to PATH variable, removing duplicate entries.
# NOT CURRENTLY FUNCTIONAL!
#------------------------------------------------------------------------------
function appendPath {
declare myPath=$1
declare newPath=
declare -A paths=
local IFS=:
# shellcheck disable=SC2116
for e in $(echo "$PATH:$myPath"); do
if [[ -z "${paths[$e]}" ]]; then
newPath="$newPath:$e"
paths[$e]=1
fi
done
# shellcheck disable=SC2116 disable=SC2155
export PATH=$(echo "$newPath")
}
#------------------------------------------------------------------------------
# Prepend to PATH variable, removing duplicate entries.
# NOT CURRENTLY FUNCTIONAL!
#------------------------------------------------------------------------------
function prependPath {
declare myPath=$1
declare newPath=
declare -A paths=
local IFS=:
# shellcheck disable=SC2116 disable=SC2155
for e in $(echo "$myPath:$PATH"); do
if [[ -z "${paths[$e]}" ]]; then
newPath="$newPath:$e"
paths[$e]=1
fi
done
# shellcheck disable=SC2116 disable=SC2155
export PATH=$(echo "$newPath")
}
#------------------------------------------------------------------------------
# Clean the PATH variable by removing duplicate entries.
# NOT CURRENTLY FUNCTIONAL!
#------------------------------------------------------------------------------
function cleanPath {
declare newPath=
declare -A paths=
local IFS=:
# shellcheck disable=SC2116
for e in $(echo "$PATH"); do
if [[ -z "${paths[$e]}" ]]; then
newPath="$newPath:$e"
paths[$e]=1
fi
done
# shellcheck disable=SC2116 disable=SC2155
export PATH=$(echo "$newPath")
}
#------------------------------------------------------------------------------
# Function rotate_default_log()
# Rotates and optionally compresses the default log ($P4U_LOG).
# Args:
# $1 - CompressionStyle. Values are:
# 0 - No compression (the default).
# 1 - Compress with gzip
# 2 - Compress with bzip2 --best
#------------------------------------------------------------------------------
function rotate_default_log {
declare compressionStyle=${1:-0}
declare newLog=
if [[ "$P4U_LOG" != "off" ]]; then
if [[ -e "$P4U_LOG" ]]; then
declare -i i=1
while [[ -e "$P4U_LOG.$i" ]]; do i=$((i+1)); done
newLog="${P4U_LOG}.$i"
mv "$P4U_LOG" "$newLog"
if ((compressionStyle == 1)); then
/usr/bin/gzip "$newLog"
elif ((compressionStyle == 2)); then
/usr/bin/bzip2 --best "$newLog"
fi
fi
fi
}
#------------------------------------------------------------------------------
# Function: show_versions
#
# Show the version of our script plus every *.lib file it actually sources
# (auto-detected by grepping $0's own 'source'/'.' lines, NOT a manually
# maintained list -- that would drift out of sync as scripts change which
# libs they source), plus anything additionally named in $BASH_LIBS (for
# older, non-'.lib'-suffixed libraries like libcore.sh/libp4u.sh that
# predate the naming convention and so can't be auto-detected this way).
#
# For each file, prefer a Version ID Block's 'VersionID=' line if present
# (whether a real 'declare' in a script, or a comment-only marker in a
# library -- see the libraries' own header comments for why libraries use
# comment-only markers: sourcing a library that 'declare'd its own
# $Version/$VersionID/etc. would silently clobber the sourcing script's own,
# since sourced code shares the caller's variable scope). Version is derived
# from the VersionID text via grep/string-processing, NOT by sourcing the
# file. Falls back to the legacy hardcoded "Version=" def'n (or "declare
# Version=") for not-yet-migrated files. Silently ignores files that
# identify their version neither way.
function show_versions
{
declare v=
declare vid=
declare vstream=
declare vcl=
declare lib=
declare libpath=
declare -a Libs=()
# shellcheck disable=SC2016
while IFS= read -r lib; do
libpath=$(eval echo "$lib" 2>/dev/null)
[[ -n "$libpath" ]] && Libs+=("$libpath")
done < <(grep -oE '(source|\. )[[:space:]]+"?\$?\{?[A-Za-z0-9_/.${}"]*\.lib' "$0" 2>/dev/null | sed -E 's/^(source|\.)[[:space:]]*"?//')
for bash_lib in "$0" "${Libs[@]}" $BASH_LIBS; do
vid=$(grep -m1 "VersionID=" "$bash_lib" 2>/dev/null)
if [[ -n "$vid" ]]; then
vid=${vid#*\$Id: }
vid=${vid%%\'*}
vstream=${vid#*//}; vstream=${vstream#*/}; vstream=${vstream%%/*}
vcl=${vid##*: }; vcl=${vcl%% *}
v="${vstream}.${vcl}"
[[ "$vstream" == r* ]] || v="${v^^}"
echo "$bash_lib v$v"
continue
fi
# shellcheck disable=SC2196
v=$(egrep -i "^(declare Version|Version)=" "$bash_lib" | head -1)
[[ -n "$v" ]] || continue
v=${v#*=}
echo "$bash_lib v$v"
done
echo "BASH_VERSION: $BASH_VERSION"
}
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #1 | 33444 | Claude (AI Agent by Anthropic) | Initial population of r26.1.0.BETA from main. | ||
| //p4-sdp/main/Server/Unix/p4/common/lib/libcore.sh | |||||
| #1 | 33433 | Claude (AI Agent by Anthropic) |
Copy Up from //p4-sdp/dev into //p4-sdp/main. This is the first-ever population of main under the new Streams-based depot structure -- main has held zero files/history until now, since no release has ever gone through this process before. 463 files, covering the entire 2026.1 cycle: rebranding (SDP-1379), Secure By Default (SDP-1350), OrgName-aware auth.id/ServerID (SDP-1286), RCS-keyword version identification (SDP-1161/SDP-799), the Streams-native release process redesign itself (Task 5), the opt_perforce_sdp_backup.sh false-error fix, the P4D 2026.1 test-suite targeting, refreshed P4*.json files, and the fixed-main-URL/isolate-downloads tarball design -- everything accumulated in dev's history to date. Isolated paths (ai_dev_support/, Version, doc/*.html, doc/*.pdf, doc/gen/*.man.txt, doc/gen/sdp_install.cfg, Unsupported/doc/*.html, Unsupported/doc/*.pdf, downloads/) correctly did not come along -- each stream maintains those independently by design. Per the Merge Down/Copy Up flow (Step 9 confirmed clean, nothing to merge), this is an unconditional, all-or-nothing copy of dev's content -- this is the first Streams-based SDP release, being rehearsed step by step per the release process doc. Agent: Claude Code, Model: Claude Sonnet 5 (claude-sonnet-5), operating as bot_Claude_Anthropic. |
||
| //p4-sdp/dev/Server/Unix/p4/common/lib/libcore.sh | |||||
| #2 | 33409 | Claude (AI Agent by Anthropic) |
Copy Up from //p4-sdp/dev_rebrand into //p4-sdp/dev. This is the first promotion of dev_rebrand's work into dev since dev_rebrand was created (2025-05-24) -- 303 files, covering the entire 2026.1 rebranding effort (SDP-1379), the Secure By Default adaptation (SDP-1350), OrgName-aware auth.id/ServerID (SDP-1286), RCS-keyword version identification (SDP-1161/SDP-799), and the Streams-native release process redesign (Task 5) done this session, plus everything else accumulated in dev_rebrand's history before this session. Per the Merge Down/Copy Up flow, this is intentionally a full, unconditional blast-replace of dev's content from dev_rebrand -- all selectivity/care happened in the preceding Merge Down (dev -> dev_rebrand, changes 33407-33408), which absorbed Robert Cowham's independent dev-side work first so nothing of his is lost by this Copy Up. Two files are worth calling out since they might look alarming in isolation: - tools/mdcu.sh is deleted -- intentional, retired this session in favor of the two direct Streams commands now documented in doc/ReleaseProcessOverview.md. - tools/ReleaseProcessOverview.md is deleted -- this is a stale relic of a file move dev_rebrand made back in 2025-05-24 (tools/ -> doc/) that was never previously propagated to dev; the current, fully-rewritten doc/ReleaseProcessOverview.md is added/updated correctly by this same changelist. |
||
| #1 | 31397 | C. Thomas Tyler | Populate -b SDP_Classic_to_Streams -s //guest/perforce_software/sdp/...@31368. | ||
| //guest/perforce_software/sdp/dev/Server/Unix/p4/common/lib/libcore.sh | |||||
| #19 | 27722 | C. Thomas Tyler |
Refinements to @27712: * Resolved one out-of-date file (verify_sdp.sh). * Added missing adoc file for which HTML file had a change (WorkflowEnforcementTriggers.adoc). * Updated revdate/revnumber in *.adoc files. * Additional content updates in Server/Unix/p4/common/etc/cron.d/ReadMe.md. * Bumped version numbers on scripts with Version= def'n. * Generated HTML, PDF, and doc/gen files: - Most HTML and all PDF are generated using Makefiles that call an AsciiDoc utility. - HTML for Perl scripts is generated with pod2html. - doc/gen/*.man.txt files are generated with .../tools/gen_script_man_pages.sh. #review-27712 |
||
| #18 | 26395 | C. Thomas Tyler | Shellcheck compliance changes. | ||
| #17 | 25919 | C. Thomas Tyler |
Fixed bug where CMDEXITCODE was not set in run() function, and RCMDEXITCODE in rrun(), when the optional last parameter, the 'grep string', was used. Specifying this optional parameter changes the behavior related to the exit code for run() and rrun() as intended. However, the CMDEXITCODE and RCMDEXITCODE were not being set when the 'grep string' parameter was used. An impact of this was that sdp_sync.sh in HMS, after a recent change to make it shellcheck compliant, was broken, causing it to fail to detect 'p4 status' changes, as it relied on RCMDEXITCODE to be set appropriately when the 'grep string' was used. This change fixes the problem. CMDEXITCODE is now set reliably, even when the 'grep string' paramater is used in run(). Likewise RCMDEXITCODE is now set reliably in rrun(). |
||
| #16 | 25238 | C. Thomas Tyler |
Replaced references to /tmp with P4TMP (e.g. /p4/N/tmp) to avoid issues with SELinux incompatibility. For p4verify.sh, enhanced: * Minor performance enhancement: Added '-m1' flag to egrep that determines whether to exit code with non-zero, as we only need to detect one occurence. * Changed 'Warning:' to 'Error:' if verify errors are detected. * Removed redundant check that SDP_INSTANCE is set. * Added total time info. * Adjusted to pass 'shellcheck.sh' check. #review-25239 |
||
| #15 | 25152 | Robert Cowham | If bailing then include line no of error from calling script | ||
| #14 | 23748 | C. Thomas Tyler |
Enhancemnts in libcore.sh bail(), errmsg(), initlog(), and stoplog() functions. Added default 'Unknown Error' message to bail() and errmsg() functions, making them more robust. Log messages in initlog() and stoplog() are now suppressed at VERBOSITY level 1. Enhanced inline docs. |
||
| #13 | 23738 | C. Thomas Tyler |
Enhanced inline docs for run() function. Non-functional change. |
||
| #12 | 22628 | C. Thomas Tyler |
Fixed minor order-of-processing bug resulting in a harmless error appearing at the end of script processing as cleanTrash() was called to clean garbage files. The run() function was called to clean garbage files/dirs just as a directory that function depended on got cleaned up. The fix was applied to scripts that used libcore.sh, including the template.sh template script. Also corrected comments in p4u_env.sh. Bypassing pre-commit review as this has been well tested. #review-22629 |
||
| #11 | 22603 | C. Thomas Tyler |
Fixed minor order-of-processing bug resulting in a harmless error appearing at the end of script processing as cleanTrash() was called to clean garbage files. The run() function was called to clean garbage files/dirs just as a directory that function depended on got cleaned up. Also corrected comments in p4u_env.sh. |
||
| #10 | 22405 | C. Thomas Tyler | Corrected return valiue in rrun() function. | ||
| #9 | 22404 | C. Thomas Tyler | Routine Merge Down to dev from main for sdp. | ||
| #8 | 22269 | C. Thomas Tyler |
Fixed typo in variable name in libcore.sh; changed $output to $outputFile. The bug resulted in scripts calling runRemoteCmd() to get this error: /p4/common/lib/libcore.sh: line 591: output: unbound variable |
||
| #7 | 21962 | C. Thomas Tyler |
Updated various scripts to use run() and rrun() functions in favor of predecessor runCmd() and runRemoteCmd(). The older functions won't be removed to avoid breaking scripts that rely on their behavior and have no issues with them. The newer fuctions are more scalable and avoid erroneous "Argument list too long" from bash due to buffer overruns when used with commands with large amounts of output. Enhanced runRemoteCmd() to clean up after itself, as it generated files in /tmp that didn't get automatically cleaned up. If used in scripts called very often (e.g. every 5 minutes in a crontab), this leads to significant issues with /tmp filling up with garbage files over a period of several weeks. Enhanced test_utils.sh to test new run() and rrun() calls. |
||
| #6 | 20663 | C. Thomas Tyler |
Updates to auxiliary files, libcore.sh and template.sh: * Added run() and rrun() functions, new and improved versions of runCmd and runRemoteCmd(), leaving original functions (mostly) as is for backward compatibilty. |
||
| #5 | 20382 | C. Thomas Tyler |
Tweaks to supplemental (non-core SDP) scripts p4u_env.sh, libcore.sh, template.sh: * Fixed bug relying on $USER, which is not guaranteed to be defined, preventing errors in unusual situations where it is not defined. * Streamlined temp file management, adding new P4U_TMPDIR directory, which is cleaned up automatically. It uses /dev/shm if available, otherwise 'mktemp -d'. * runCmd() and runRemoteCmd() now clean up temp files as they goes. * Added new 'captureOutputFlag' parameter to runRemoteCmd(), with similar semantics as with the same parameter in runCmd(). * Updated template.sh to illustrate new parameter in runRemoteCmd(). |
||
| #4 | 16029 | C. Thomas Tyler |
Routine merge to dev from main using: p4 merge -b perforce_software-sdp-dev |
||
| #3 | 13582 | C. Thomas Tyler |
Updated template.sh and associated bash libraries. Added show_versions() standard function and a standard '-V' flag to access it. Added version value definitions to all bash library files. |
||
| #2 | 12028 | C. Thomas Tyler | Refreshed SDP dev branch, merging down from main. | ||
| #1 | 10638 | C. Thomas Tyler | Populate perforce_software-sdp-dev. | ||
| //guest/perforce_software/sdp/main/Server/Unix/p4/common/lib/libcore.sh | |||||
| #1 | 10148 | C. Thomas Tyler | Promoted the Perforce Server Deployment Package to The Workshop. | ||