Files
Varaverk/Tools/conf_populate.sh
T
Gmer4Lfe 369a9e6c19 Platform adapter: rename System_Essentials, add Plugin/unraid/adapter.sh, wire call sites
- Rename unRAID_Essentials/ → System_Essentials/ (git detects as rename)
- Add Plugin/unraid/adapter.sh: 13 platform_*() functions providing OS-agnostic API
  for storage health, service management, mover, user scripts, notifications,
  disk temps, and platform command validation
- Update load_config.sh: detect PLATFORM (unraid/truenas/unknown), export SCRIPTS_DIR,
  auto-source Plugin/$PLATFORM/adapter.sh after common.sh
- Wire all call sites: replace direct rc.d, pgrep/pkill, var.ini, dynamix.cfg,
  disks.ini, and validate_unraid_cmd calls with platform_*() functions across
  watchdogs, orchestrators, and System_Essentials scripts
- Update all documentation: rename refs, update webgui escalation logic,
  add platform adapter section to Plugin README, update main README with
  portability vision and corrected self-healing stack description
2026-06-04 18:14:34 -04:00

227 lines
11 KiB
Bash
Executable File

#!/bin/bash
# ==============================================================================================
# ============================= Conf Auto-Populate =============================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Reads credentials and settings from locally running services and writes
# them into the local host conf. Safe to run multiple times — only populates
# EMPTY fields, never overwrites existing values unless --overwrite is passed.
#
# After populating, pushes the updated conf to all partners via conf_sync.sh
# so they have the fresh keys in their /tmp/.vv/ cache immediately.
#
# ==============================================================================================
# AUTO-DETECTED FIELDS
# ==============================================================================================
#
# HOSTN_RADARR_API_KEY from Radarr config.xml (found via docker volume mount)
# HOSTN_SONARR_API_KEY from Sonarr config.xml
# HOSTN_LIDARR_API_KEY from Lidarr config.xml
# HOSTN_SLSKD_API_KEY from slskd config.yml
# HOSTN_SABNZBD_API_KEY from sabnzbd.ini
# HOSTN_EMBY_CONTAINER fuzzy match from docker ps
# HOSTN_JELLYFIN_CONTAINER fuzzy match from docker ps
# HOSTN_RADARR_MOVIE_ROOT from Radarr rootFolder API
# HOSTN_SONARR_TV_ROOT from Sonarr rootFolder API
# HOSTN_LIDARR_MUSIC_ROOT from Lidarr rootFolder API
# HOSTN_SYS_WATCHDOG_NIC from ip route default gateway interface
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# conf_populate.sh Populate empty fields only
# conf_populate.sh --overwrite Overwrite all detected fields (re-sync after arr key rotation)
# conf_populate.sh --dry-run Show what would be written, no changes
# conf_populate.sh --log Verbose output
# conf_populate.sh --no-push Skip pushing to partners after update
#
# ==============================================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../load_config.sh"
OVERWRITE=false
NO_PUSH=false
FILTERED_ARGS=()
for arg in "$@"; do
case "$arg" in
--overwrite) OVERWRITE=true ;;
--no-push) NO_PUSH=true ;;
*) FILTERED_ARGS+=("$arg") ;;
esac
done
parse_args "${FILTERED_ARGS[@]}"
detect_hosts
[[ "$EUID" -ne 0 ]] && { error "Must be run as root"; exit 1; }
CONF_FILE="$SCRIPTS_ROOT/Configurations/${MY_ID,,}.conf"
[[ ! -f "$CONF_FILE" ]] && { error "Conf file not found: $CONF_FILE"; exit 1; }
UPDATED=0
SKIPPED=0
echo ""
echo "━━━ $ICON_GEAR Conf Auto-Populate — $MY_ID ($LOCAL_SERVER_NAME) ━━━"
echo ""
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
[[ "$OVERWRITE" == true ]] && warn "OVERWRITE mode — existing values will be replaced"
# ── Helper: write a var into conf if empty (or --overwrite) ──────────────────
_set_conf_var() {
local var_name="$1" value="$2" label="$3"
[[ -z "$value" ]] && return
# Check current value in conf
local current
current=$(grep -oP "(?<=^\s*${var_name}=\")[^\"]*" "$CONF_FILE" 2>/dev/null | head -1)
if [[ -n "$current" ]] && [[ "$OVERWRITE" == false ]]; then
log "$label: already set (${current:0:8}…) — skipping"
(( SKIPPED++ ))
return
fi
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would set $var_name = ${value:0:8}…"
return
fi
# Update or append the var line
if grep -q "^\s*${var_name}=" "$CONF_FILE"; then
sed -i "s|^\(\s*${var_name}\s*=\s*\)\"[^\"]*\"|\1\"${value}\"|" "$CONF_FILE"
else
printf '\n %s="%s"\n' "$var_name" "$value" >> "$CONF_FILE"
fi
info "$label: set ✅"
(( UPDATED++ ))
}
# ── Helper: find arr config dir via docker volume mount ───────────────────────
# Looks for a container matching the pattern, then reads its /config volume path.
# Falls back to DOCKER_APPDATA_BASE/<ContainerName> if volume not found.
_arr_config_dir() {
local pattern="$1"
local container_name
container_name=$(docker ps -a --format '{{.Names}}' 2>/dev/null | \
grep -im1 "^${pattern}")
[[ -z "$container_name" ]] && return 1
local config_path
config_path=$(docker inspect "$container_name" 2>/dev/null | \
jq -r '.[0].Mounts[]? | select(.Destination == "/config") | .Source' 2>/dev/null | head -1)
[[ -z "$config_path" ]] && config_path="${DOCKER_APPDATA_BASE:-/mnt/user/appdata}/${container_name}"
[[ -d "$config_path" ]] && echo "$config_path" || return 1
}
# ── Helper: read XML tag value ────────────────────────────────────────────────
_xml_val() {
local file="$1" tag="$2"
grep -oP "(?<=<${tag}>)[^<]+" "$file" 2>/dev/null | head -1
}
# ==============================================================================================
# ── Arr API keys + root paths ─────────────────────────────────────────────────────────────────
# ==============================================================================================
for arr in radarr sonarr lidarr; do
arr_upper="${arr^^}"
config_dir=$(_arr_config_dir "$arr") || {
log "${arr_upper}: no running container found — skipping"
continue
}
config_xml="${config_dir}/config.xml"
if [[ ! -f "$config_xml" ]]; then
log "${arr_upper}: config.xml not found at $config_xml — skipping"
continue
fi
key=$(_xml_val "$config_xml" "ApiKey")
port=$(_xml_val "$config_xml" "Port")
url_base="http://localhost:${port:-$(case $arr in radarr) echo 7878;; sonarr) echo 8989;; lidarr) echo 8686;; esac)}"
_set_conf_var "${MY_ID}_${arr_upper}_API_KEY" "$key" "${arr_upper} API key"
# Root paths from arr's own rootFolder API
if [[ -n "$key" ]]; then
local api_ver; case "$arr" in lidarr) api_ver="v1" ;; *) api_ver="v3" ;; esac
root_json=$(curl -sf --max-time 5 \
-H "X-Api-Key: $key" "${url_base}/api/${api_ver}/rootfolder" 2>/dev/null)
root_path=$(echo "$root_json" | jq -r '.[0].path // empty' 2>/dev/null)
case "$arr" in
radarr) _set_conf_var "${MY_ID}_RADARR_MOVIE_ROOT" "$root_path" "Radarr movie root" ;;
sonarr) _set_conf_var "${MY_ID}_SONARR_TV_ROOT" "$root_path" "Sonarr TV root" ;;
lidarr) _set_conf_var "${MY_ID}_LIDARR_MUSIC_ROOT" "$root_path" "Lidarr music root" ;;
esac
fi
done
# ==============================================================================================
# ── SABnzbd API key ───────────────────────────────────────────────────────────────────────────
# ==============================================================================================
sab_dir=$(_arr_config_dir "sabnzbd") && {
sab_ini=$(find "$sab_dir" -maxdepth 2 -name "sabnzbd.ini" 2>/dev/null | head -1)
if [[ -f "$sab_ini" ]]; then
sab_key=$(grep -oP '(?<=^api_key\s*=\s*)\S+' "$sab_ini" 2>/dev/null | head -1)
_set_conf_var "${MY_ID}_SABNZBD_API_KEY" "$sab_key" "SABnzbd API key"
fi
}
# ==============================================================================================
# ── slskd API key ─────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
slskd_dir=$(_arr_config_dir "slskd") && {
slskd_yml=$(find "$slskd_dir" -maxdepth 2 -name "*.yml" -o -name "*.yaml" 2>/dev/null | head -1)
if [[ -f "$slskd_yml" ]]; then
slskd_key=$(grep -oP '(?<=api_key:\s)[\w-]+' "$slskd_yml" 2>/dev/null | head -1)
[[ -z "$slskd_key" ]] && \
slskd_key=$(grep -oP '(?<=apikey:\s)[\w-]+' "$slskd_yml" 2>/dev/null | head -1)
_set_conf_var "${MY_ID}_SLSKD_API_KEY" "$slskd_key" "slskd API key"
fi
}
# ==============================================================================================
# ── Container names ───────────────────────────────────────────────────────────────────────────
# ==============================================================================================
for pattern in "emby" "jellyfin"; do
container=$(docker ps -a --format '{{.Names}}' 2>/dev/null | grep -im1 "^${pattern}")
[[ -z "$container" ]] && continue
case "$pattern" in
emby) _set_conf_var "${MY_ID}_EMBY_CONTAINER" "$container" "Emby container name" ;;
jellyfin) _set_conf_var "${MY_ID}_JELLYFIN_CONTAINER" "$container" "Jellyfin container name" ;;
esac
done
# ==============================================================================================
# ── Network interface ─────────────────────────────────────────────────────────────────────────
# ==============================================================================================
nic=$(ip route show default 2>/dev/null | grep -oP '(?<=dev )\S+' | head -1)
_set_conf_var "${MY_ID}_SYS_WATCHDOG_NIC" "$nic" "Default NIC"
# ==============================================================================================
# ── Summary + push ────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
echo ""
echo "━━━━━ $ICON_SUMMARY Conf Populate Summary ━━━━━"
echo " Updated: $UPDATED field(s)"
echo " Skipped: $SKIPPED already set"
echo " Conf: $CONF_FILE"
echo "━━━━━━━━━━━━━━━━━━━━━"
if [[ "$UPDATED" -gt 0 ]] && [[ "$DRY_RUN" == false ]] && [[ "$NO_PUSH" == false ]]; then
echo ""
info "Pushing updated conf to partners..."
bash "$SCRIPTS_ROOT/System_Essentials/conf_sync.sh" --push-only "${EXTRA_FLAGS[@]}" || true
fi