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