#!/bin/bash # ============================================================================================== # ============================= Conf Cache Save ================================================ # ============================================================================================== # # PURPOSE # ───────────────────────────────────────────────────────────────────────────── # Snapshots the partner conf RAM cache to $PERSISTENT_CONF_CACHE on # array stop. Survives reboot. Used by conf_cache_restore.sh at next array start # to reload partner vars into RAM when the partner is unreachable at boot time. # # Runs as the first step in ARRAY_STOP_SCRIPTS — while the RAM cache is fresh # and before anything else changes. If partner is reachable at next start, # conf_sync.sh gets a fresh copy and the backup is never used. If partner is # down, the backup fills the gap so fallback.sh has the vars it needs. # # Only partner confs are saved — own conf is always on disk. # Path adapts to storage mode: $SCRIPTS_DIR/.cache/vv/d (internal or appdata). # # ============================================================================================== # OPERATIONAL MODEL # ============================================================================================== # # 1. Gates — PARTNERSHIP_ENABLED, and a validated PERSISTENT_CONF_CACHE path # 2. No RAM cache present → exit 0, nothing to snapshot # 3. For each host*.conf in the RAM cache: # own conf → skip (always on disk, never needs saving) # partner conf → copy to $PERSISTENT_CONF_CACHE, mode 600 # # Counterpart to conf_cache_restore.sh, which consumes and then clears this backup # at the next array start. # # ============================================================================================== # DESIGN PRINCIPLES # ============================================================================================== # # Save at Shutdown, Not at Boot # The backup is created at array stop while the RAM cache is at its freshest. # By the next boot the partner may be unreachable — but the conf is already # preserved and available the moment conf_cache_restore.sh runs. # # Partner Only # Own conf is on disk — always present, never needs saving. Only partner confs # live in RAM and can be missing at the next boot. # # ============================================================================================== # OPERATIONAL SAFEGUARDS # ============================================================================================== # # Root Enforcement # Writes into $PERSISTENT_CONF_CACHE under the plugin directory. # # Lock Acquisition # acquire_lock prevents this racing conf_cache_restore.sh or the conf cache # watchdog over the same backup directory. # # Partnership Gate # require_partnership exits early if PARTNERSHIP_ENABLED=false. # # Host Detection # detect_hosts() determines which confs are partner confs and which is our own. # # Cache Path Sanity Guard # PERSISTENT_CONF_CACHE is validated as an absolute path at least three levels # deep before anything is written. It is built from ${SCRIPTS_DIR}; if that were # unset the copy target would collapse to "/host2.conf", dropping partner # passwords and API keys at the filesystem root. # # No-Cache Guard # Exits cleanly if the RAM cache is missing or empty — nothing to snapshot is a # normal state, not an error. # # Own-Conf Exclusion # Our own conf is never written into the partner backup. Restoring it later # would overwrite live local config with a stale copy. # # Credential File Permissions # The backup directory is created 700 and each conf written 600. These files # carry partner NPM/lldap passwords and API keys and must not inherit the # default umask on a path that survives reboot. # # Dry Run Support # --dry-run reports every file it would write and writes none. # # ============================================================================================== # CONFIGURATION # ============================================================================================== # # master.conf # # PERSISTENT_CONF_CACHE # Reboot-surviving destination for the partner conf backup. Built from # ${SCRIPTS_DIR}, so it follows the active storage mode. # # CONF_RAM_CACHE_DIR # Source RAM cache (tmpfs, /tmp/varaverk/conf) populated by conf_sync.sh. # # PARTNERSHIP_ENABLED # Checked via require_partnership(). # # ============================================================================================== # RUNTIME MODES # ============================================================================================== # # conf_cache_save.sh # Snapshot partner confs from the RAM cache to the persistent backup. # Runs first in ARRAY_STOP_SCRIPTS. # # conf_cache_save.sh --dry-run # Report what would be saved without writing anything # # conf_cache_save.sh --log # Verbose per-file output # # ============================================================================================== SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../load_config.sh" parse_args "$@" if [[ "$EUID" -ne 0 ]]; then error "Must be run as root" exit 1 fi acquire_lock detect_hosts require_partnership RAM_CACHE="$CONF_RAM_CACHE_DIR" SAVE_DIR="${PERSISTENT_CONF_CACHE:-}" # Credentials get written here. An empty SAVE_DIR would make the cp target "/host2.conf", # dropping partner passwords and API keys at the filesystem root. _slashes="${SAVE_DIR//[^\/]/}" if [[ -z "$SAVE_DIR" || "$SAVE_DIR" != /* || "${#_slashes}" -lt 3 ]]; then error "PERSISTENT_CONF_CACHE is unset or unsafe ('${SAVE_DIR:-unset}') — refusing to save partner confs" notify "conf_cache_save aborted on $(hostname) — PERSISTENT_CONF_CACHE is '${SAVE_DIR:-unset}'" \ "Conf Cache Save" "warning" exit 1 fi unset _slashes [[ "$DRY_RUN" == true ]] && warn "DRY RUN — no files will be written" if [[ ! -d "$RAM_CACHE" ]]; then log "No RAM conf cache found — nothing to save" exit 0 fi saved=0 for conf in "$RAM_CACHE"/host*.conf; do [[ -f "$conf" ]] || continue base="$(basename "$conf")" is_own_conf_file "$base" && continue if [[ "$DRY_RUN" == true ]]; then warn "DRY RUN — would save $base → $SAVE_DIR/" (( saved++ )) continue fi # Partner confs carry credentials — restrict on write rather than leaving them at the # default umask on a path that survives reboot. mkdir -p "$SAVE_DIR" && chmod 700 "$SAVE_DIR" if cp "$conf" "$SAVE_DIR/$base" && chmod 600 "$SAVE_DIR/$base"; then echo "Saved $base → $SAVE_DIR ✅" (( saved++ )) else warn "Failed to save $base" fi done if [[ "$saved" -eq 0 ]]; then log "No partner confs in RAM cache — nothing saved" else echo "Conf cache saved: $saved partner conf(s) → $SAVE_DIR" fi