# shellcheck shell=bash
#------------------------------------------------------------------------------
# Function: get_mount_point
#
# Purpose:
# Given a path (file or directory), determine the mount point of the
# filesystem containing it.
#
# Behavior:
# * If the exact path doesn't exist, walks up to the nearest existing parent.
# * Canonicalizes that existing path (prefers `realpath`, else falls back).
# * Uses `findmnt -T` to resolve the mount point (preferred), falling back
# to `df --output=target` if needed.
# * On success: echoes the mount point and returns 0.
# * On error: calls errmsg(), increments ErrorCount (via your function),
# and returns non-zero.
#
# Dependencies (present on RHEL 7+ / Ubuntu 18.04+):
# * `findmnt` (util-linux), `df` and `realpath` (coreutils).
# * msg() and errmsg() functions must be defined by caller.
#
# Usage:
# mp=$(get_mount_point /some/path) || echo "Failed"
#
# Notes:
# * `findmnt -T` correctly handles bind mounts and is preferred.
# * We intentionally canonicalize to avoid surprises with symlinks.
#------------------------------------------------------------------------------
function get_mount_point () {
local input="$1"
if [[ -z "$input" ]]; then
errmsg "No path specified."
return 1
fi
# Walk up until we find an existing path (handles non-existent file paths).
local probe="$input"
while [[ ! -e "$probe" ]]; do
local parent
parent=$(dirname -- "$probe")
if [[ -z "$parent" || "$parent" == "$probe" ]]; then
errmsg "No existing parent found for: $input"
return 1
fi
probe="$parent"
# Stop at root dir even if -e fails (shouldn't in normal cases).
[[ "$probe" == "/" ]] && break
done
# Canonicalize the existing path.
local canon
if command -v realpath >/dev/null 2>&1; then
# -e: require existing; resolves symlinks cleanly
if ! canon=$(realpath -e -- "$probe" 2>/dev/null); then
errmsg "Failed to canonicalize path: $probe"
return 1
fi
elif command -v readlink >/dev/null 2>&1 && readlink -f / >/dev/null 2>&1; then
# Linux readlink with -f behaves like realpath -e
if ! canon=$(readlink -f -- "$probe" 2>/dev/null); then
errmsg "Failed to canonicalize path: $probe"
return 1
fi
else
# Portable fallback: cd + pwd -P (physical)
if ! canon=$(cd "$probe" 2>/dev/null && pwd -P); then
errmsg "Failed to canonicalize path: $probe"
return 1
fi
fi
# Determine mount point: prefer findmnt, fallback to df.
local mount_point=""
if command -v findmnt >/dev/null 2>&1; then
mount_point=$(findmnt -no TARGET -T "$canon" 2>/dev/null)
fi
if [[ -z "$mount_point" ]]; then
# Fallback using df (GNU coreutils)
mount_point=$(df --output=target "$canon" 2>/dev/null | tail -n 1)
fi
if [[ -z "$mount_point" || ! -d "$mount_point" ]]; then
errmsg "Could not determine mount point for: $input"
return 1
fi
msg "$mount_point"
return 0
}
#------------------------------------------------------------------------------
# Function: find_sdp_instance
#
# Purpose:
# Determine the SDP instance name to use.
#
# Behavior:
# * If an explicit instance arg is provided, validates it.
# * Otherwise, scans SDPRoot for subdirs that contain a "bin/" directory.
# - If exactly one candidate is found, echoes it and returns 0.
# - If multiple candidates are found, prefers "1" if present;
# otherwise, selects the lexicographically first (deterministic).
# - If zero are found, emits an error and returns non-zero.
#
# Usage:
# inst=$(find_sdp_instance "/p4" "1") || exit 1
# inst=$(find_sdp_instance "/p4") || exit 1
#------------------------------------------------------------------------------
function find_sdp_instance () {
local sdp_root="${1:-/p4}"
local wanted_inst="${2:-}"
if [[ ! -d "$sdp_root" ]]; then
errmsg "SDPRoot not found or not a directory: $sdp_root"
return 1
fi
if [[ -n "$wanted_inst" ]]; then
if [[ -d "$sdp_root/$wanted_inst/bin" ]]; then
msg "$wanted_inst"
return 0
fi
errmsg "Instance '$wanted_inst' not found under $sdp_root (missing $sdp_root/$wanted_inst/bin)."
return 1
fi
# Discover candidates: any subdir containing bin/
local -a candidates=()
while IFS= read -r -d '' bin_dir; do
candidates+=("$(basename -- "$(dirname -- "$bin_dir")")")
done < <(find "$sdp_root" -mindepth 2 -maxdepth 2 -type d -name bin -print0 2>/dev/null)
case "${#candidates[@]}" in
0)
errmsg "No SDP instances found under $sdp_root (no */bin directories)."
return 1
;;
1)
msg "${candidates[0]}"
return 0
;;
*)
# Prefer instance "1" if present
local inst
for inst in "${candidates[@]}"; do
if [[ "$inst" == "1" ]]; then
msg "1"
return 0
fi
done
# Otherwise, pick lexicographically first (stable/deterministic)
# shellcheck disable=SC2207
candidates=($(printf '%s\n' "${candidates[@]}" | LC_ALL=C sort))
msg "${candidates[0]}"
return 0
;;
esac
}
#------------------------------------------------------------------------------
# Function: find_p4depots_probe_path
#
# Purpose:
# Given SDPRoot and (optionally) an instance, echo a path that resides on the
# P4Depots volume. This path is suitable to pass to get_mount_point().
#
# Behavior:
# * Validates SDPRoot.
# * Determines instance:
# - If explicit instance is given, validates it (requires <inst>/bin).
# - Else scans SDPRoot for instances (subdirs with a "bin/" dir).
# If multiple are found, prefers "1"; otherwise picks the
# lexicographically first (deterministic).
# * Checks, in order of preference, under the chosen instance:
# 1) depots/ (server)
# 2) cache/ (proxy)
# 3) first matching checkpoints*/ (server variants)
# 4) logs/ (broker)
# Echoes the first that exists (dir or symlink), with a trailing slash.
# * If none of the above exist, emits an error and returns non-zero.
#
# Usage:
# probe=$(find_p4depots_probe_path "/p4" "1") || exit 1
# probe=$(find_p4depots_probe_path "/p4") || exit 1
#
# Notes:
# * No fallback to $SDPRoot/$Instance/; fail-fast if structure is unexpected.
#------------------------------------------------------------------------------
function find_p4depots_probe_path () {
local sdp_root="${1:-/p4}"
local inst="${2:-}"
if [[ ! -d "$sdp_root" ]]; then
errmsg "SDPRoot not found or not a directory: $sdp_root"
return 1
fi
# If instance provided, validate it.
if [[ -n "$inst" ]]; then
if [[ ! -d "$sdp_root/$inst/bin" ]]; then
errmsg "Instance '$inst' not found under $sdp_root (missing $sdp_root/$inst/bin)."
return 1
fi
else
# Discover instances: any subdir containing bin/
local -a candidates=()
local bin_dir
while IFS= read -r -d '' bin_dir; do
candidates+=("$(basename -- "$(dirname -- "$bin_dir")")")
done < <(find "$sdp_root" -mindepth 2 -maxdepth 2 -type d -name bin -print0 2>/dev/null)
# If there are no instances, return an error. If there is exactly one, than that is
# the instance. If there are multiple, pick instance '1' (the default) if it exists,
# otherwise the first valid one.
case "${#candidates[@]}" in
0)
errmsg "No SDP instances found under $sdp_root (no */bin directories)."
return 1
;;
1)
inst="${candidates[0]}"
;;
*)
# Prefer "1" if present
local cand
for cand in "${candidates[@]}"; do
if [[ "$cand" == "1" ]]; then
inst="1"
break
fi
done
# Otherwise pick lexicographically first (deterministic)
if [[ -z "$inst" ]]; then
# shellcheck disable=SC2207
candidates=($(printf '%s\n' "${candidates[@]}" | LC_ALL=C sort))
inst="${candidates[0]}"
fi
;;
esac
fi
local base="$sdp_root/$inst"
if [[ ! -d "$base" ]]; then
errmsg "Instance directory missing: $base"
return 1
fi
# Helper: echo path with ensured trailing slash.
local _echo_with_slash
_echo_with_slash() {
local p="$1"
if [[ "$p" == */ ]]; then
msg "$p"
else
msg "$p/"
fi
}
# 1) depots/
if [[ -e "$base/depots" ]]; then
_echo_with_slash "$base/depots"
return 0
fi
# 2) cache/ (proxy)
if [[ -e "$base/cache" ]]; then
_echo_with_slash "$base/cache"
return 0
fi
# 3) checkpoints*/ (handle checkpoints and checkpoints.* variants)
local cp
shopt -s nullglob
for cp in "$base"/checkpoints "$base"/checkpoints.*; do
if [[ -e "$cp" ]]; then
_echo_with_slash "$cp"
shopt -u nullglob
return 0
fi
done
shopt -u nullglob
# 4) logs - last resort, should exist for all server types, even standaloen brokers.
if [[ -e "$base/logs" ]]; then
_echo_with_slash "$base/logs"
return 0
fi
errmsg "No suitable P4Depots-related path found under $base (expected depots/, cache/, or checkpoints*/)."
return 1
}
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #1 | 32135 | C. Thomas Tyler |
Released SDP 2025.1.32133 (2025/10/29). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
| //guest/perforce_software/sdp/dev/Server/Unix/p4/common/lib/utils.lib | |||||
| #2 | 31911 | C. Thomas Tyler | Fixed issue with opt_perforce_sdp_backup.sh operating on a standalone broker. | ||
| #1 | 31886 | C. Thomas Tyler |
Refactoring non-trivial functions into a library utils.lib. Added test script. |
||