#!/bin/bash
# Shared checkpoint ownership for backup/recovery scripts. Sourcing this helper
# changes no shell options and installs no traps. Shared die() releases its
# owned marker; abrupt termination and other handlers may leave it in place.
function ckp_lock_fail () {
   if declare -F die >/dev/null; then die "$*";
   elif declare -F bail >/dev/null; then bail "$*";
   else printf '%s\n' "$*" >&2; exit 1; fi
}

#------------------------------------------------------------------------------
# Acquire the checkpoint semaphore before initializing/rotating shared logs.
# noclobber uses exclusive file creation: exactly one concurrent caller wins.
# A legacy marker, stale marker, or interrupted write is deliberately blocking.
# Operators must investigate and remove it manually; PID reuse/shared mounts
# make automatic stale-lock removal unsafe. Completion and shared die() release
# only the acquiring shell's marker with its matching owner token.
# Optional "quiet" keeps rotate_journal's contention path silent.
#------------------------------------------------------------------------------
function ckp_running() {
   local LockFile="${LOGS}/ckp_running.txt"
   local Token="${HOSTNAME:-unknown}:${BASHPID:-$$}:$RANDOM:$RANDOM"
   local Owner=
   if [[ -n "${CkpLockToken:-}" && "${CkpLockPid:-}" == "${BASHPID:-$$}" &&
         "${CkpLockSubshell:-0}" == "${BASH_SUBSHELL:-0}" ]]; then
      IFS= read -r Owner < "$LockFile" 2>/dev/null || Owner=
      [[ "$Owner" == "$CkpLockToken" ]] && return 0
      echo "Checkpoint lock ownership changed: $LockFile. Aborting." >&2
      exit 1
   fi
   if (set -o noclobber; printf '%s\n' "$Token" > "$LockFile") 2>/dev/null; then
      CkpLockToken=$Token
      CkpLockPid=${BASHPID:-$$}
      CkpLockSubshell=${BASH_SUBSHELL:-0}
      return 0
   fi
   if [[ -e "$LockFile" || -L "$LockFile" ]]; then
      # Do not append to or rotate the active owner's checkpoint log. The
      # separate skip log is append-only; quiet callers preserve cron silence.
      if [[ "${1:-}" != quiet ]]; then
         (
            LOGFILE="$LOGS/checkpoint_skipped.log"
            if declare -F sdp_log_init >/dev/null; then
               sdp_log_init "${0##*/}"
               sdp_set_status skipped
               sdp_add_field skip_reason checkpoint_semaphore_exists
            fi
            printf '%s\n' "Skipping: checkpoint semaphore $LockFile already exists." >> "$LOGFILE"
         )
      fi
      exit 0
   fi
   echo "Cannot create checkpoint semaphore $LockFile. Aborting." >&2
   exit 1
}

#------------------------------------------------------------------------------
# Remove only this shell's successful-run semaphore. Fail closed if an operator
# or another process replaced it while this run was active.
#------------------------------------------------------------------------------
function ckp_complete() {
   local Owner=
   if [[ -z "${CkpLockToken:-}" || "${CkpLockPid:-}" != "${BASHPID:-$$}" ||
         "${CkpLockSubshell:-0}" != "${BASH_SUBSHELL:-0}" ]]; then
      ckp_lock_fail "Cannot release a checkpoint semaphore this shell does not own."
   fi
   IFS= read -r Owner < "${LOGS}/ckp_running.txt" 2>/dev/null || Owner=
   [[ "$Owner" == "$CkpLockToken" ]] || ckp_lock_fail "Checkpoint semaphore ownership changed; leaving it untouched."
   rm -- "${LOGS}/ckp_running.txt" 2>> "$LOGFILE" || ckp_lock_fail "Cannot release the checkpoint semaphore."
   CkpLockToken=
   CkpLockPid=
   CkpLockSubshell=
}

