From 27bfc21cb036a93ddf04c449c0308bb52e87247d Mon Sep 17 00:00:00 2001 From: Gmer4Lfe Date: Sun, 14 Jun 2026 00:59:19 -0400 Subject: [PATCH] Platform-agnostic refactor: eliminate OS-specific hardcodes from core scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All bash scripts are now platform-neutral. Unraid-specific paths, commands, and service checks moved to Plugin/unraid/adapter.sh. Core scripts call platform_*() functions exclusively — no direct OS paths in runtime logic. New adapter functions: platform_storage_path, platform_webui_install_path, platform_scripts_dir_probe_cmd, platform_setup_db_path, platform_storage_healthy, platform_is_service_enabled, platform_get_temp_thresholds, platform_disk_states_path, platform_rebuild_container, platform_push_conf, platform_push_setup_state, platform_get_templates_dir, platform_send_os_notification. Partnership services stack (Emby/Jellyfin/Seerr/SeerrFin) added as third onboarding stack alongside auth and arr stacks. --- Docker_Essentials/docker_network_connect.sh | 1 - Docker_Essentials/docker_update.sh | 2 +- Docker_Essentials/docker_update_remaining.sh | 2 +- Fallback/fallback.sh | 4 - Fallback/fallback_test.sh | 4 - Media/arrs_failed_stalled_recovery.sh | 4 - Media/lidarr_cleanup.sh | 4 - Media/media_cleaner.sh | 4 - Media/media_shares_permissions.sh | 4 - Media/play_state_sync.sh | 28 ++-- Media/radarr_cleanup.sh | 4 - Media/sonarr_cleanup.sh | 4 - Monitors/backup_verify.sh | 4 - Monitors/bandwidth_monitor.sh | 4 - Monitors/cert_monitor.sh | 4 - Monitors/emby_session_report.sh | 4 - Monitors/smart_health.sh | 4 - Monitors/system_tuning_monitor.sh | 4 - Monitors/weekly_health_digest.sh | 4 - Monitors/zfs_memory_snapshot.sh | 4 - Orchestrators/array_started.sh | 4 - Orchestrators/array_stopping.sh | 4 - Orchestrators/critical_sync_maintenance.sh | 4 - Orchestrators/daily_sync_maintenance.sh | 4 - .../intermediate_sync_maintenance.sh | 4 - Orchestrators/watchdog_orchestrator.sh | 2 +- Orchestrators/weekly_sync_maintenance.sh | 4 - Partnership/onboard_cancel.sh | 2 +- Partnership/partnership_manager.sh | 118 ++------------ Partnership/partnership_offboard.sh | 3 + Partnership/partnership_onboard.sh | 154 ++++++++++-------- Partnership/partnership_transfer.sh | 11 +- Partnership/ssh_setup.sh | 5 - Plugin/unraid/Partnership/containers.sh | 92 ++++++++++- Plugin/unraid/adapter.sh | 130 ++++++++++++++- Plugin/unraid/include/common.php | 12 +- Plugin/unraid/pages/monitor.php | 32 ++-- Rsync/rsync.sh | 4 - System_Essentials/clear_logs.sh | 4 - System_Essentials/conf_sync.sh | 2 +- System_Essentials/docker_syslog_filter.sh | 4 - System_Essentials/inotify_tuning.sh | 4 - System_Essentials/rsync_stop.sh | 4 - System_Essentials/server_reboot.sh | 4 - Tools/bulk_permissions_repair.sh | 4 - Tools/container_data_export.sh | 4 - Tools/emby_database_repair.sh | 4 - Tools/fallback_state_reset.sh | 4 - Tools/ramdisk_stop.sh | 4 - Tools/smart_long_test.sh | 4 - Tools/watchdog_skip_list_manager.sh | 4 - Tools/zfs_pool_scrub.sh | 4 - Transcodes/ramdisk_setup.sh | 4 - Transcodes/transcode_cleanup.sh | 4 - Transcodes/transcode_manager.sh | 4 - Watchdogs/System/network_watchdog.sh | 4 - Watchdogs/docker_watchdog.sh | 6 - Watchdogs/stability_watchdog.sh | 4 - Watchdogs/system_watchdog.sh | 4 - common.sh | 83 +++++----- git_pull_execute.sh | 2 +- 61 files changed, 411 insertions(+), 444 deletions(-) diff --git a/Docker_Essentials/docker_network_connect.sh b/Docker_Essentials/docker_network_connect.sh index bf792ac..d731082 100755 --- a/Docker_Essentials/docker_network_connect.sh +++ b/Docker_Essentials/docker_network_connect.sh @@ -131,7 +131,6 @@ fi detect_hosts # Validate unRAID notify script — used for network creation alerts -platform_require_cmd "/usr/local/emhttp/plugins/dynamix/scripts/notify" "" "" "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" # Docker daemon check — network operations are useless if daemon is hung DOCKER_TIMEOUT=15 diff --git a/Docker_Essentials/docker_update.sh b/Docker_Essentials/docker_update.sh index 25cd296..104a549 100755 --- a/Docker_Essentials/docker_update.sh +++ b/Docker_Essentials/docker_update.sh @@ -336,7 +336,7 @@ if [[ ${#UPDATED[@]} -gt 0 ]]; then continue fi log "$ICON_SYNC Rebuilding $container from template on new image..." - if /usr/local/emhttp/plugins/dynamix.docker.manager/scripts/rebuild_container "$container" >/dev/null 2>&1; then + if platform_rebuild_container "$container"; then log "$ICON_DONE $container rebuilt ✅" REBUILT+=("$container") else diff --git a/Docker_Essentials/docker_update_remaining.sh b/Docker_Essentials/docker_update_remaining.sh index 5117b77..0ae1343 100755 --- a/Docker_Essentials/docker_update_remaining.sh +++ b/Docker_Essentials/docker_update_remaining.sh @@ -251,7 +251,7 @@ if [[ ${#UPDATED[@]} -gt 0 ]]; then fi log "$ICON_RUNNING $container — recreating from template on new image..." - if /usr/local/emhttp/plugins/dynamix.docker.manager/scripts/rebuild_container "$container" >/dev/null 2>&1; then + if platform_rebuild_container "$container"; then if verify_running "$container"; then log "$ICON_DONE $container recreated and running ✅" RESTARTED+=("$container") diff --git a/Fallback/fallback.sh b/Fallback/fallback.sh index 671d672..52409d7 100755 --- a/Fallback/fallback.sh +++ b/Fallback/fallback.sh @@ -292,10 +292,6 @@ detect_hosts resolve_remote_ip # Validate unRAID notify script — used throughout for state change notifications -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" [[ "$DRY_RUN" == true ]] && warn "DRY RUN — no container or DDNS changes will be made" diff --git a/Fallback/fallback_test.sh b/Fallback/fallback_test.sh index ec25d75..611731f 100755 --- a/Fallback/fallback_test.sh +++ b/Fallback/fallback_test.sh @@ -171,10 +171,6 @@ platform_require_cmd \ "--version" "iptables" \ "iptables" || { error "iptables not found — required for connectivity simulation"; exit 1; } -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" if [[ ! -f "$FALLBACK_SCRIPT" ]]; then error "fallback.sh not found at $FALLBACK_SCRIPT" diff --git a/Media/arrs_failed_stalled_recovery.sh b/Media/arrs_failed_stalled_recovery.sh index dbe3c67..066358b 100755 --- a/Media/arrs_failed_stalled_recovery.sh +++ b/Media/arrs_failed_stalled_recovery.sh @@ -112,10 +112,6 @@ if ! command -v jq >/dev/null 2>&1; then fi log "jq found" -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" [[ "$DRY_RUN" == true ]] && warn "DRY RUN — no items will be blocklisted or searched" diff --git a/Media/lidarr_cleanup.sh b/Media/lidarr_cleanup.sh index 01e314a..b378f78 100755 --- a/Media/lidarr_cleanup.sh +++ b/Media/lidarr_cleanup.sh @@ -153,10 +153,6 @@ if ! command -v jq >/dev/null 2>&1; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" # Lock before detect_hosts — large library scans take time, wait mode appropriate [[ -n "${LIDARR_LOCK_WARN_AGE:-}" ]] && LOCK_WARN_AGE="$LIDARR_LOCK_WARN_AGE" diff --git a/Media/media_cleaner.sh b/Media/media_cleaner.sh index 7217811..ae9263a 100755 --- a/Media/media_cleaner.sh +++ b/Media/media_cleaner.sh @@ -117,10 +117,6 @@ acquire_lock "wait" # detect_hosts() sets MY_ID and aliases HOST*_ANIME/MEDIA_CLEAN_FOLDERS detect_hosts -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" # Resolve profile folders and patterns case "$PROFILE" in diff --git a/Media/media_shares_permissions.sh b/Media/media_shares_permissions.sh index dfc9727..edd67d5 100755 --- a/Media/media_shares_permissions.sh +++ b/Media/media_shares_permissions.sh @@ -70,10 +70,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock "wait" diff --git a/Media/play_state_sync.sh b/Media/play_state_sync.sh index d8e2b58..4718523 100755 --- a/Media/play_state_sync.sh +++ b/Media/play_state_sync.sh @@ -301,24 +301,22 @@ for lname in "${!USER_MAP[@]}"; do for _si in "${!U_IDX[@]}"; do _uid="${U_UID[$_si]}" - _endpoint="Users/${_uid}/Items?Recursive=true&Fields=ProviderIds,UserData,Type,ParentIndexNumber,IndexNumber,SeriesName&IncludeItemTypes=${SYNC_TYPES}&Filters=IsPlayed${_date_filter}&Limit=5000" + # All played items — no limit, covers both date-stamped and batch-marked (null date) entries + _endpoint="Users/${_uid}/Items?Recursive=true&Fields=ProviderIds,UserData,Type,ParentIndexNumber,IndexNumber,SeriesName&IncludeItemTypes=${SYNC_TYPES}&Filters=IsPlayed&SortBy=DatePlayed&SortOrder=Descending" _resp=$(_api_get "${SRV_URL[$_si]}" "${SRV_KEY[$_si]}" "$_endpoint") if [[ -z "$_resp" ]]; then warn " ${SRV_NAME[$_si]} — failed to fetch items for $lname" continue fi - # Also fetch items with resume position (not yet marked played) - _endpoint2="Users/${_uid}/Items?Recursive=true&Fields=ProviderIds,UserData,Type,ParentIndexNumber,IndexNumber,SeriesName&IncludeItemTypes=${SYNC_TYPES}&SortBy=DatePlayed&SortOrder=Descending&Filters=IsResumable${_date_filter}&Limit=500" + # Resume positions (not yet marked played) + _endpoint2="Users/${_uid}/Items?Recursive=true&Fields=ProviderIds,UserData,Type,ParentIndexNumber,IndexNumber,SeriesName&IncludeItemTypes=${SYNC_TYPES}&SortBy=DatePlayed&SortOrder=Descending&Filters=IsResumable" _resp2=$(_api_get "${SRV_URL[$_si]}" "${SRV_KEY[$_si]}" "$_endpoint2") # Combine and deduplicate by Id - if [[ -n "$_resp2" ]]; then - _combined=$(printf '%s\n%s' "$_resp" "$_resp2" | jq -s \ - '[.[0].Items // [], .[1].Items // []] | add // [] | unique_by(.Id)' 2>/dev/null) - else - _combined=$(echo "$_resp" | jq '.Items // []' 2>/dev/null) - fi + [[ -z "$_resp2" ]] && _resp2='{"Items":[]}' + _combined=$(printf '%s\n%s' "$_resp" "$_resp2" | jq -s \ + '[.[0].Items // [], .[1].Items // []] | add // [] | unique_by(.Id)' 2>/dev/null) _count=$(echo "$_combined" | jq 'length' 2>/dev/null || echo 0) log " ${SRV_NAME[$_si]} — $_count item(s) with state for $lname" @@ -422,8 +420,16 @@ for lname in "${!USER_MAP[@]}"; do _their_played="${E_PLAYED[$_si]:-false}" # Skip if they already have the same/newer state - if [[ "$_their_epoch" -ge "$_auth_epoch" ]] && \ - [[ "$_their_played" == "$_auth_played" ]]; then + _skip=false + if [[ "$_auth_played" == "true" ]]; then + # Played: skip if target is already marked played with same/newer date + [[ "$_their_epoch" -ge "$_auth_epoch" && "$_their_played" == "true" ]] && _skip=true + else + # Resume only: skip if target already has same or more ticks + # (posting ticks via /UserData doesn't set LastPlayedDate, so epoch comparison is useless here) + [[ "${E_TICKS[$_si]:-0}" -ge "${_auth_ticks:-0}" && "$_their_played" == "false" ]] && _skip=true + fi + if [[ "$_skip" == true ]]; then log " SKIP $_pkey → ${SRV_NAME[$_si]} already up to date" (( TOTAL_SKIPPED++ )) continue diff --git a/Media/radarr_cleanup.sh b/Media/radarr_cleanup.sh index e3b8c12..f8cbb48 100755 --- a/Media/radarr_cleanup.sh +++ b/Media/radarr_cleanup.sh @@ -141,10 +141,6 @@ if ! command -v jq >/dev/null 2>&1; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock "wait" TMP_DIR="/tmp/radarr_cleanup_$$" diff --git a/Media/sonarr_cleanup.sh b/Media/sonarr_cleanup.sh index 507a57c..103fca4 100755 --- a/Media/sonarr_cleanup.sh +++ b/Media/sonarr_cleanup.sh @@ -141,10 +141,6 @@ if ! command -v jq >/dev/null 2>&1; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock "wait" TMP_DIR="/tmp/sonarr_cleanup_$$" diff --git a/Monitors/backup_verify.sh b/Monitors/backup_verify.sh index ec8e5ff..816f193 100755 --- a/Monitors/backup_verify.sh +++ b/Monitors/backup_verify.sh @@ -119,10 +119,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Monitors/bandwidth_monitor.sh b/Monitors/bandwidth_monitor.sh index f2403fe..b65c333 100755 --- a/Monitors/bandwidth_monitor.sh +++ b/Monitors/bandwidth_monitor.sh @@ -134,10 +134,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" # detect_hosts() sets MY_ID for report header detect_hosts diff --git a/Monitors/cert_monitor.sh b/Monitors/cert_monitor.sh index bfdbdc3..998c8e5 100755 --- a/Monitors/cert_monitor.sh +++ b/Monitors/cert_monitor.sh @@ -110,10 +110,6 @@ platform_require_cmd \ "version" "OpenSSL" \ "openssl" || { error "openssl not found — required for certificate checks"; exit 1; } -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Monitors/emby_session_report.sh b/Monitors/emby_session_report.sh index f290348..4056352 100755 --- a/Monitors/emby_session_report.sh +++ b/Monitors/emby_session_report.sh @@ -111,10 +111,6 @@ if ! command -v jq >/dev/null 2>&1; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Monitors/smart_health.sh b/Monitors/smart_health.sh index f5c362c..1904bf6 100755 --- a/Monitors/smart_health.sh +++ b/Monitors/smart_health.sh @@ -104,10 +104,6 @@ platform_require_cmd \ exit 1 } -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Monitors/system_tuning_monitor.sh b/Monitors/system_tuning_monitor.sh index 108e8f3..0d4d6e5 100755 --- a/Monitors/system_tuning_monitor.sh +++ b/Monitors/system_tuning_monitor.sh @@ -113,10 +113,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Monitors/weekly_health_digest.sh b/Monitors/weekly_health_digest.sh index d273563..e58e020 100755 --- a/Monitors/weekly_health_digest.sh +++ b/Monitors/weekly_health_digest.sh @@ -126,10 +126,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" platform_require_cmd \ "$(command -v openssl 2>/dev/null || echo /usr/bin/openssl)" \ diff --git a/Monitors/zfs_memory_snapshot.sh b/Monitors/zfs_memory_snapshot.sh index dceb3ad..cee374d 100755 --- a/Monitors/zfs_memory_snapshot.sh +++ b/Monitors/zfs_memory_snapshot.sh @@ -125,10 +125,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Orchestrators/array_started.sh b/Orchestrators/array_started.sh index 2616f72..db76bb2 100755 --- a/Orchestrators/array_started.sh +++ b/Orchestrators/array_started.sh @@ -74,10 +74,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Orchestrators/array_stopping.sh b/Orchestrators/array_stopping.sh index 1bd257f..406c5d7 100755 --- a/Orchestrators/array_stopping.sh +++ b/Orchestrators/array_stopping.sh @@ -53,10 +53,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Orchestrators/critical_sync_maintenance.sh b/Orchestrators/critical_sync_maintenance.sh index 772bf18..16c8eb4 100755 --- a/Orchestrators/critical_sync_maintenance.sh +++ b/Orchestrators/critical_sync_maintenance.sh @@ -59,10 +59,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock "strict" diff --git a/Orchestrators/daily_sync_maintenance.sh b/Orchestrators/daily_sync_maintenance.sh index 8b036aa..0947dc6 100755 --- a/Orchestrators/daily_sync_maintenance.sh +++ b/Orchestrators/daily_sync_maintenance.sh @@ -83,10 +83,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" if ! command -v docker &>/dev/null; then error "Docker command not found" diff --git a/Orchestrators/intermediate_sync_maintenance.sh b/Orchestrators/intermediate_sync_maintenance.sh index 16646d0..59cf637 100755 --- a/Orchestrators/intermediate_sync_maintenance.sh +++ b/Orchestrators/intermediate_sync_maintenance.sh @@ -71,10 +71,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Orchestrators/watchdog_orchestrator.sh b/Orchestrators/watchdog_orchestrator.sh index d65de7b..a326923 100755 --- a/Orchestrators/watchdog_orchestrator.sh +++ b/Orchestrators/watchdog_orchestrator.sh @@ -125,7 +125,7 @@ if ! platform_storage_healthy; then echo "Array not started — skipping watchdog cycle" exit 0 fi -log "$ICON_DISK Array: /mnt/user mounted (shfs) ✅" +log "$ICON_DISK Array: $(platform_storage_path) mounted ✅" # ============================================================================================== # ━━━ Startup Grace ━━━ diff --git a/Orchestrators/weekly_sync_maintenance.sh b/Orchestrators/weekly_sync_maintenance.sh index 4df9208..7bccb2f 100755 --- a/Orchestrators/weekly_sync_maintenance.sh +++ b/Orchestrators/weekly_sync_maintenance.sh @@ -76,10 +76,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Partnership/onboard_cancel.sh b/Partnership/onboard_cancel.sh index 42d49d8..d255790 100755 --- a/Partnership/onboard_cancel.sh +++ b/Partnership/onboard_cancel.sh @@ -88,7 +88,7 @@ if [[ "$DIRECTION" == "h1" || "$DIRECTION" == "both" ]]; then timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \ -o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes root@"$MIRROR_IP" \ "sed -i \"|${KEY_BLOB}|d\" /root/.ssh/authorized_keys 2>/dev/null - sed -i \"/^${MIRROR_ID}_PHASE\|^${MIRROR_ID}_KEY_READY/d\" /boot/config/varaverk_setup.db 2>/dev/null + sed -i \"/^${MIRROR_ID}_PHASE\|^${MIRROR_ID}_KEY_READY/d\" $(platform_setup_db_path) 2>/dev/null echo ok" 2>/dev/null | grep -q ok && { log "HOST1 key removed from $MIRROR authorized_keys ✅" H1_DONE=true diff --git a/Partnership/partnership_manager.sh b/Partnership/partnership_manager.sh index b12465b..00645b3 100755 --- a/Partnership/partnership_manager.sh +++ b/Partnership/partnership_manager.sh @@ -155,6 +155,10 @@ # Containers parked here during partnership, restarted on offboard. # Aliased by detect_hosts() → PARTNERSHIP_OWN_CONTAINERS # +# HOST*_PARTNERSHIP_SERVICES_STACK +# Shared services XMLs (Emby, Jellyfin, Seerr, SeerrFin) deployed on mirror during onboard. +# Aliased by detect_hosts() → PARTNERSHIP_SERVICES_STACK +# # ============================================================================================== # RUNTIME MODES # ============================================================================================== @@ -187,6 +191,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../load_config.sh" +source "$SCRIPT_DIR/../Plugin/$PLATFORM/Partnership/containers.sh" SSH_TIMEOUT=15 BLOCKLIST_FILE="${PARTNERSHIP_BLOCKLIST_FILE:-${STATE_DIR:-/boot/config}/partnership_blocklist.db}" @@ -235,11 +240,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" - if ! command -v docker &>/dev/null; then error "Docker command not found" exit 1 @@ -368,73 +368,6 @@ read_remote_state() { "cat '$remote_file' 2>/dev/null" 2>/dev/null } -# Reconfigure a container's WebUI on the remote server -reconfigure_webui() { - local container="$1" port="$2" target_ip="$3" - local ssh_key="$4" remote_ip="$5" label="${6:-remote}" - - log "Reconfiguring $container WebUI → ${target_ip}:${port} on $label..." - - if [[ "$DRY_RUN" == true ]]; then - warn "DRY RUN — would reconfigure $container WebUI to http://${target_ip}:${port}/" - return 0 - fi - - local template - template=$(timeout "$SSH_TIMEOUT" ssh -i "$ssh_key" \ - -o ConnectTimeout="$SSH_TIMEOUT" root@"$remote_ip" \ - "grep -rl '' /boot/config/plugins/dockerMan/templates-user/ 2>/dev/null | \ - xargs grep -l '\"$container\"' 2>/dev/null | head -1" 2>/dev/null) - - if [[ -z "$template" ]]; then - warn "$container template not found on $label — WebUI needs manual reconfiguration" - return 1 - fi - - timeout "$SSH_TIMEOUT" ssh -i "$ssh_key" \ - -o ConnectTimeout="$SSH_TIMEOUT" root@"$remote_ip" \ - "sed -i 's|.*|http://${target_ip}:${port}/|g' '$template'" \ - 2>/dev/null && \ - log "$container → http://${target_ip}:${port}/ ✅" || { - error "Failed to reconfigure $container WebUI on $label" - return 1 - } -} - -# Reconfigure local auth WebUIs to target IP -reconfigure_local_webuis() { - local target_ip="$1" - log "Reconfiguring local auth WebUIs → ${target_ip}..." - - local failures=0 - for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]}"; do - [[ -z "$entry" ]] && continue - local container="${entry%%|*}" - local port="${entry##*|}" - - local template - template=$(grep -rl '' \ - /boot/config/plugins/dockerMan/templates-user/ 2>/dev/null | \ - xargs grep -l "\"$container\"" 2>/dev/null | head -1) - - if [[ -z "$template" ]]; then - warn "$container template not found locally" - (( failures++ )) - continue - fi - - if [[ "$DRY_RUN" == true ]]; then - warn "DRY RUN — would reconfigure $container → http://${target_ip}:${port}/" - continue - fi - - sed -i "s|.*|http://${target_ip}:${port}/|g" \ - "$template" 2>/dev/null && \ - log "$container → http://${target_ip}:${port}/ ✅" || \ - { error "Failed to reconfigure $container"; (( failures++ )); } - done - return $failures -} get_tailscale_device_id() { local hostname="$1" @@ -941,15 +874,16 @@ revoke_emby_admin() { } check_both_healthy() { - mountpoint -q /mnt/user 2>/dev/null || { error "Local array not healthy"; return 1; } + platform_storage_healthy || { error "Local array not healthy"; return 1; } - local mirror_ip + local mirror_ip _spath mirror_ip=$(resolve_tailscale_ip "$MIRROR") [[ -z "$mirror_ip" ]] && { error "Cannot resolve $MIRROR Tailscale IP"; return 1; } + _spath=$(platform_storage_path) timeout "$SSH_TIMEOUT" ssh -i "$MIRROR_SSH_KEY" \ -o ConnectTimeout="$SSH_TIMEOUT" root@"$mirror_ip" \ - "mountpoint -q /mnt/user && timeout 10 docker ps" >/dev/null 2>&1 || { + "mountpoint -q '$_spath' && timeout 10 docker ps" >/dev/null 2>&1 || { error "Mirror $MIRROR not healthy" return 1 } @@ -972,12 +906,7 @@ do_final_sync() { fi done else - bash "$SCRIPT_DIR/../Rsync/rsync.sh" \ - "/mnt/user/appdata-Fallback/Critical-Data" \ - --profile=critical-fallback --log - bash "$SCRIPT_DIR/../Rsync/rsync.sh" \ - "/mnt/user/Media_Server/Emby" \ - --profile=emby-fallback --log + warn "CRITICAL_SYNC_SHARES is empty — skipping final sync (configure in host*.conf)" fi else warn "DRY RUN — would run final critical sync (${#CRITICAL_SYNC_SHARES[@]:-hardcoded} shares)" @@ -1234,26 +1163,14 @@ if [[ "$MODE" == "onboard" ]]; then echo "" echo "Enabling partnership in master.conf..." if [[ "$DRY_RUN" == false ]]; then - if command -v php &>/dev/null; then - php -r " -require_once '/usr/local/emhttp/plugins/varaverk/include/config.php'; -\$master = vv_read_conf_raw('master.conf'); -\$master = preg_replace('/^\s*PARTNERSHIP_ENABLED\s*=.*/m', 'PARTNERSHIP_ENABLED=true', \$master); -if (vv_write_conf_raw('master.conf', \$master)) { - \$results = vv_push_master_conf(); - foreach (\$results as \$r) { - echo \$r['host'] . ': ' . (\$r['ok'] ? 'pushed' : 'FAILED — ' . \$r['error']) . PHP_EOL; - } - echo 'PARTNERSHIP_ENABLED=true' . PHP_EOL; -} else { - echo 'write_failed' . PHP_EOL; -} -" 2>/dev/null | grep -v '^$' | while IFS= read -r line; do - [[ "$line" == "PARTNERSHIP_ENABLED=true" ]] && log "PARTNERSHIP_ENABLED=true in master.conf ✅" || log "$line" - done + local _master_conf="${SCRIPTS_ROOT:-$(dirname "$SCRIPT_DIR")}/master.conf" + if grep -q "^[[:space:]]*PARTNERSHIP_ENABLED=" "$_master_conf" 2>/dev/null; then + sed -i "s|^[[:space:]]*PARTNERSHIP_ENABLED=.*|PARTNERSHIP_ENABLED=true|" "$_master_conf" else - warn "php not available — set PARTNERSHIP_ENABLED=true in master.conf manually" + echo "PARTNERSHIP_ENABLED=true" >> "$_master_conf" fi + log "PARTNERSHIP_ENABLED=true in master.conf ✅" + platform_push_conf | while IFS= read -r line; do log "$line"; done else warn "DRY RUN — would set PARTNERSHIP_ENABLED=true in master.conf and push" fi @@ -1267,8 +1184,7 @@ if (vv_write_conf_raw('master.conf', \$master)) { else echo "${flag_key}=true" >> "$local_state_file" fi - command -v php &>/dev/null && \ - php -r "require_once '/usr/local/emhttp/plugins/varaverk/include/config.php'; vv_push_setup_state();" 2>/dev/null || true + platform_push_setup_state log "${MY_ID}_LOCAL_DONE=true written to setup.db ✅" else warn "DRY RUN — would write ${MY_ID}_LOCAL_DONE=true" diff --git a/Partnership/partnership_offboard.sh b/Partnership/partnership_offboard.sh index 87e2878..41337dd 100755 --- a/Partnership/partnership_offboard.sh +++ b/Partnership/partnership_offboard.sh @@ -50,6 +50,9 @@ # HOST*_PARTNERSHIP_ARR_STACK # Arr container XMLs — same cleanup logic as auth stack. # +# HOST*_PARTNERSHIP_SERVICES_STACK +# Shared services XMLs (Emby, Jellyfin, Seerr, SeerrFin) — same cleanup logic. +# # ============================================================================================== # RUNTIME MODES # ============================================================================================== diff --git a/Partnership/partnership_onboard.sh b/Partnership/partnership_onboard.sh index 055c891..08bea50 100755 --- a/Partnership/partnership_onboard.sh +++ b/Partnership/partnership_onboard.sh @@ -18,16 +18,18 @@ # Step 1: SSH key setup — generate keypair, copy to owner, update conf # Owner completes the rest remotely. Mirror is done. # -# OWNER PATH (8 steps) -# Step 1: SSH key setup — generate keypair, install on mirror, update conf -# Step 2: Stop mirror auth — stop mirror's existing auth containers before replacing -# Step 3: Deploy auth stack — push XMLs, pull images, create + start on mirror -# Mariadb/Redis health-checked before Authelia deploys -# Step 4: Stop mirror arr — stop mirror's existing arr containers before replacing -# Step 5: Deploy arr stack — push arr XMLs, pull images, create + start on mirror -# Step 6: Partnership onboard — configure WebUIs → owner IP, write state, Emby -# Step 7: Arr bootstrap — bidirectional library sync (arr_sync.sh) -# Step 8: Conf push — push master.conf + setup state to all listed hosts +# OWNER PATH (10 steps) +# Step 1: SSH key setup — generate keypair, install on mirror, update conf +# Step 2: Stop mirror auth — stop mirror's existing auth containers before replacing +# Step 3: Deploy auth stack — push XMLs, pull images, create + start on mirror +# Mariadb/Redis health-checked before Authelia deploys +# Step 4: Stop mirror arr — stop mirror's existing arr containers before replacing +# Step 5: Deploy arr stack — push arr XMLs, pull images, create + start on mirror +# Step 6: Stop mirror services — stop mirror's existing services containers before replacing +# Step 7: Deploy services stack — push Emby/Jellyfin/Seerr XMLs, pull images, create + start +# Step 8: Partnership onboard — configure WebUIs → owner IP, write state, Emby +# Step 9: Arr bootstrap — bidirectional library sync (arr_sync.sh) +# Step 10: Conf push — push master.conf + setup state to all listed hosts # # ============================================================================================== # DESIGN PRINCIPLES @@ -98,6 +100,16 @@ # Same rule as PARTNERSHIP_REPLACE_CONTAINERS: defined in mirror's own conf, never HOST1's. # Aliased by detect_hosts() → PARTNERSHIP_ARR_REPLACE_CONTAINERS (on the mirror) # +# HOST*_PARTNERSHIP_SERVICES_STACK +# XML filenames to push and deploy on the mirror as its shared services stack. +# Includes Emby, Jellyfin, Seerr, SeerrFin. Leave empty to skip services stack deploy. +# Aliased by detect_hosts() → PARTNERSHIP_SERVICES_STACK +# +# HOST*_PARTNERSHIP_SERVICES_REPLACE_CONTAINERS +# Services containers to stop on the mirror before deploying the services stack. +# Same rule as PARTNERSHIP_REPLACE_CONTAINERS: defined in mirror's own conf, never HOST1's. +# Aliased by detect_hosts() → PARTNERSHIP_SERVICES_REPLACE_CONTAINERS (on the mirror) +# # ============================================================================================== # RUNTIME MODES # ============================================================================================== @@ -118,10 +130,13 @@ # Skip auth stack stop + deploy (Steps 3-4) # # Partnership/partnership_onboard.sh --skip-arr-stack -# Skip arr stack stop + deploy (Steps 5-6) +# Skip arr stack stop + deploy (Steps 4-5) +# +# Partnership/partnership_onboard.sh --skip-services-stack +# Skip services stack stop + deploy (Steps 6-7) # # Partnership/partnership_onboard.sh --skip-arr-sync -# Skip arr library bootstrap (Step 8) +# Skip arr library bootstrap (Step 9) # # Partnership/partnership_onboard.sh --phase1-only # OWNER only: SSH key exchange + conf push. Safe to run before HOST2 has Varaverk. @@ -145,6 +160,7 @@ source "$SCRIPTS_ROOT/Plugin/$PLATFORM/Partnership/containers.sh" SKIP_SSH=false SKIP_AUTH_STACK=false SKIP_ARR_STACK=false +SKIP_SERVICES_STACK=false SKIP_ARR_SYNC=false PHASE1_ONLY=false # OWNER: SSH + conf push only (HOST2 not yet installed) PHASE2_ONLY=false # OWNER: containers/arr/onboard only (triggered by HOST2 after it onboards) @@ -154,8 +170,9 @@ for arg in "$@"; do case "$arg" in --skip-ssh) SKIP_SSH=true ;; --skip-auth-stack) SKIP_AUTH_STACK=true ;; - --skip-arr-stack) SKIP_ARR_STACK=true ;; - --skip-arr-sync) SKIP_ARR_SYNC=true ;; + --skip-arr-stack) SKIP_ARR_STACK=true ;; + --skip-services-stack) SKIP_SERVICES_STACK=true ;; + --skip-arr-sync) SKIP_ARR_SYNC=true ;; --phase1-only) PHASE1_ONLY=true ;; --phase2-only) PHASE2_ONLY=true; SKIP_SSH=true ;; *) FILTERED_ARGS+=("$arg") ;; @@ -210,8 +227,7 @@ write_onboard_phase() { else echo "${key}=true" >> "$state_file" fi - command -v php &>/dev/null && \ - php -r "require_once '/usr/local/emhttp/plugins/varaverk/include/config.php'; vv_push_setup_state();" 2>/dev/null || true + platform_push_setup_state } echo "" @@ -293,11 +309,12 @@ if [[ "$AM_MIRROR" == true ]]; then PHASE2_TRIGGERED=false if [[ -n "$OWNER_IP" ]]; then - # Read OWNER's SCRIPTS_DIR from their varaverk.cfg — don't assume same path as mirror + # Read OWNER's SCRIPTS_DIR via platform probe command — don't assume same path as mirror + _probe_cmd=$(platform_scripts_dir_probe_cmd) OWNER_SCRIPTS_DIR=$(timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \ -o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes root@"$OWNER_IP" \ - 'grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d "\"'"'"'" 2>/dev/null' 2>/dev/null | tr -d '[:space:]') - OWNER_SCRIPTS_DIR="${OWNER_SCRIPTS_DIR:-/boot/config/plugins/varaverk}" + "$_probe_cmd" 2>/dev/null | tr -d '[:space:]') + OWNER_SCRIPTS_DIR="${OWNER_SCRIPTS_DIR:-$SCRIPTS_DIR}" if [[ "$DRY_RUN" == true ]]; then warn "DRY RUN — would SSH to $OWNER ($OWNER_IP) and trigger Phase 2" @@ -349,6 +366,10 @@ STEP_STOP_ARR_OK=true STEP_ARR_OK=true ARR_DEPLOYED=0 ARR_FAILED=0 +STEP_STOP_SERVICES_OK=true +STEP_SERVICES_OK=true +SERVICES_DEPLOYED=0 +SERVICES_FAILED=0 ONBOARD_OK=false ARR_SYNC_OK=false MASTER_PUSH_OK=false @@ -444,23 +465,11 @@ if [[ "$PHASE1_ONLY" == true ]]; then if [[ "$DRY_RUN" == true ]]; then warn "DRY RUN — would push master.conf + state file to $MIRROR" CONF_PUSH_OK=true - elif ! command -v php &>/dev/null; then - warn "php not available — push master.conf manually via Scheduler → master.conf → Save Conf" else - push_output=$(php -r " -require_once '/usr/local/emhttp/plugins/varaverk/include/config.php'; -\$results = vv_push_master_conf(); -vv_push_setup_state(); -if (empty(\$results)) { echo 'no remote hosts'; exit(0); } -\$failed = 0; -foreach (\$results as \$r) { - echo \$r['host'] . ': ' . (\$r['ok'] ? 'pushed' : 'FAILED — ' . \$r['error']) . PHP_EOL; - if (!\$r['ok']) \$failed++; -} -exit(\$failed > 0 ? 1 : 0); -" 2>/dev/null) + push_output=$(platform_push_conf) push_rc=$? - echo "$push_output" + [[ -n "$push_output" ]] && echo "$push_output" + platform_push_setup_state if [[ $push_rc -eq 0 ]]; then log "Conf push complete ✅" CONF_PUSH_OK=true @@ -533,7 +542,7 @@ else stop_mirror_stack "PARTNERSHIP_ARR_REPLACE_CONTAINERS" "arr stack" fi -# ── Step 6: Deploy arr stack on mirror ─────────────────────────────────────────────────────── +# ── Step 5: Deploy arr stack on mirror ─────────────────────────────────────────────────────── echo "" echo "━━━ Step 5 — Deploy Arr Stack on Mirror ━━━" @@ -547,9 +556,36 @@ else [[ "$ARR_FAILED" -gt 0 ]] && STEP_ARR_OK=false fi -# ── Step 7: Partnership onboard ─────────────────────────────────────────────────────────────── +# ── Step 6: Stop mirror's existing services stack ───────────────────────────────────────────── echo "" -echo "━━━ Step 6 — Partnership Onboard ━━━" +echo "━━━ Step 6 — Stop Mirror Services Stack ━━━" + +if [[ "$SKIP_SERVICES_STACK" == true ]]; then + warn "Skipping (--skip-services-stack)" +elif [[ ${#PARTNERSHIP_SERVICES_STACK[@]} -eq 0 ]]; then + log "PARTNERSHIP_SERVICES_STACK not configured — skipping services stack deploy" + SKIP_SERVICES_STACK=true +else + stop_mirror_stack "PARTNERSHIP_SERVICES_REPLACE_CONTAINERS" "services stack" +fi + +# ── Step 7: Deploy services stack on mirror ─────────────────────────────────────────────────── +echo "" +echo "━━━ Step 7 — Deploy Services Stack on Mirror ━━━" + +if [[ "$SKIP_SERVICES_STACK" == true ]]; then + warn "Skipping (--skip-services-stack)" +else + deploy_xml_stack PARTNERSHIP_SERVICES_STACK + SERVICES_DEPLOYED=$_STACK_DEPLOYED + SERVICES_FAILED=$_STACK_FAILED + echo "Services stack: $SERVICES_DEPLOYED deployed, $SERVICES_FAILED failed" + [[ "$SERVICES_FAILED" -gt 0 ]] && STEP_SERVICES_OK=false +fi + +# ── Step 8: Partnership onboard ─────────────────────────────────────────────────────────────── +echo "" +echo "━━━ Step 8 — Partnership Onboard ━━━" if bash "$SCRIPTS_ROOT/Partnership/partnership_manager.sh" --onboard "${EXTRA_FLAGS[@]}"; then echo "Partnership onboard complete ✅" @@ -559,9 +595,9 @@ else ONBOARD_OK=false fi -# ── Step 8: Arr library bootstrap ───────────────────────────────────────────────────────────── +# ── Step 9: Arr library bootstrap ───────────────────────────────────────────────────────────── echo "" -echo "━━━ Step 7 — Arr Library Bootstrap ━━━" +echo "━━━ Step 9 — Arr Library Bootstrap ━━━" if [[ "$ONBOARD_OK" == false ]]; then warn "Skipping — onboard did not complete" @@ -577,34 +613,22 @@ else warn "Re-run Media/arr_sync.sh once all arr containers are live" fi -# ── Step 9: Push master.conf to all listed hosts ────────────────────────────────────────────── +# ── Step 10: Push master.conf to all listed hosts ───────────────────────────────────────────── # SSH is now established and all partners have the plugin installed. # Push the authoritative master.conf so every listed host is in sync immediately. echo "" -echo "━━━ $ICON_GEAR Step 8 — master.conf Push ━━━" +echo "━━━ $ICON_GEAR Step 10 — master.conf Push ━━━" if [[ "$ONBOARD_OK" == false ]]; then warn "Skipping — onboard did not complete" elif [[ "$DRY_RUN" == true ]]; then warn "DRY RUN — would push master.conf to all listed hosts" MASTER_PUSH_OK=true -elif ! command -v php &>/dev/null; then - warn "php not available — push master.conf manually via Scheduler → master.conf → Save Conf" else - push_output=$(php -r " -require_once '/usr/local/emhttp/plugins/varaverk/include/config.php'; -\$results = vv_push_master_conf(); -vv_push_setup_state(); -if (empty(\$results)) { echo 'no remote hosts'; exit(0); } -\$failed = 0; -foreach (\$results as \$r) { - echo \$r['host'] . ': ' . (\$r['ok'] ? 'pushed' : 'FAILED — ' . \$r['error']) . PHP_EOL; - if (!\$r['ok']) \$failed++; -} -exit(\$failed > 0 ? 1 : 0); -" 2>/dev/null) + push_output=$(platform_push_conf) push_rc=$? - echo "$push_output" + [[ -n "$push_output" ]] && echo "$push_output" + platform_push_setup_state if [[ $push_rc -eq 0 ]]; then echo "master.conf sync complete ✅" MASTER_PUSH_OK=true @@ -628,14 +652,16 @@ echo "" _ok() { [[ "$1" == true ]] && echo "✅" || echo "❌"; } _skip() { [[ "$1" == true ]] && echo "skipped" || echo "$(_ok "$2")"; } -echo " Step 1 — SSH keys: $(_skip "$SKIP_SSH" "$STEP_SSH_OK")" -echo " Step 2 — Stop auth: $(_skip "$SKIP_AUTH_STACK" "$STEP_STOP_AUTH_OK")" -echo " Step 3 — Auth stack: $( [[ "$SKIP_AUTH_STACK" == true ]] && echo "skipped" || echo "${AUTH_DEPLOYED} deployed, ${AUTH_FAILED} failed" )" -echo " Step 4 — Stop arr: $(_skip "$SKIP_ARR_STACK" "$STEP_STOP_ARR_OK")" -echo " Step 5 — Arr stack: $( [[ "$SKIP_ARR_STACK" == true ]] && echo "skipped" || echo "${ARR_DEPLOYED} deployed, ${ARR_FAILED} failed" )" -echo " Step 6 — Onboard: $(_ok "$ONBOARD_OK")" -echo " Step 7 — Arr bootstrap: $( [[ "$SKIP_ARR_SYNC" == true || "$ONBOARD_OK" == false ]] && echo "skipped" || echo "$(_ok "$ARR_SYNC_OK")" )" -echo " Step 8 — Conf push: $( [[ "$ONBOARD_OK" == false ]] && echo "skipped" || echo "$(_ok "$MASTER_PUSH_OK")" )" +echo " Step 1 — SSH keys: $(_skip "$SKIP_SSH" "$STEP_SSH_OK")" +echo " Step 2 — Stop auth: $(_skip "$SKIP_AUTH_STACK" "$STEP_STOP_AUTH_OK")" +echo " Step 3 — Auth stack: $( [[ "$SKIP_AUTH_STACK" == true ]] && echo "skipped" || echo "${AUTH_DEPLOYED} deployed, ${AUTH_FAILED} failed" )" +echo " Step 4 — Stop arr: $(_skip "$SKIP_ARR_STACK" "$STEP_STOP_ARR_OK")" +echo " Step 5 — Arr stack: $( [[ "$SKIP_ARR_STACK" == true ]] && echo "skipped" || echo "${ARR_DEPLOYED} deployed, ${ARR_FAILED} failed" )" +echo " Step 6 — Stop services: $(_skip "$SKIP_SERVICES_STACK" "$STEP_STOP_SERVICES_OK")" +echo " Step 7 — Services stack: $( [[ "$SKIP_SERVICES_STACK" == true ]] && echo "skipped" || echo "${SERVICES_DEPLOYED} deployed, ${SERVICES_FAILED} failed" )" +echo " Step 8 — Onboard: $(_ok "$ONBOARD_OK")" +echo " Step 9 — Arr bootstrap: $( [[ "$SKIP_ARR_SYNC" == true || "$ONBOARD_OK" == false ]] && echo "skipped" || echo "$(_ok "$ARR_SYNC_OK")" )" +echo " Step 10 — Conf push: $( [[ "$ONBOARD_OK" == false ]] && echo "skipped" || echo "$(_ok "$MASTER_PUSH_OK")" )" echo "" if [[ "$ONBOARD_OK" == true ]]; then diff --git a/Partnership/partnership_transfer.sh b/Partnership/partnership_transfer.sh index 492b411..bfa8ab6 100755 --- a/Partnership/partnership_transfer.sh +++ b/Partnership/partnership_transfer.sh @@ -126,11 +126,6 @@ if ! command -v docker &>/dev/null; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" - detect_hosts OWNER_ID="${PARTNERSHIP_OWNER_HOST:-HOST1}" @@ -291,11 +286,11 @@ if [[ "$DRY_RUN" == false ]]; then # Push updated master.conf to new owner so both servers agree immediately. # master.conf is shared — host-specific credentials live in host*.conf. + _probe_cmd=$(platform_scripts_dir_probe_cmd) _REMOTE_SD=$(ssh -i "$SSH_KEY" -o ConnectTimeout=5 -o StrictHostKeyChecking=no \ - "root@${MIRROR_IP}" \ - "grep -m1 '^SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'" \ + "root@${MIRROR_IP}" "$_probe_cmd" \ 2>/dev/null | tr -d '[:space:]') - _REMOTE_SD="${_REMOTE_SD:-/boot/config/plugins/varaverk}" + _REMOTE_SD="${_REMOTE_SD:-$SCRIPTS_DIR}" scp -i "$SSH_KEY" \ -o ConnectTimeout="$SSH_TIMEOUT" \ -o StrictHostKeyChecking=no \ diff --git a/Partnership/ssh_setup.sh b/Partnership/ssh_setup.sh index 259415f..3bbefe9 100755 --- a/Partnership/ssh_setup.sh +++ b/Partnership/ssh_setup.sh @@ -70,11 +70,6 @@ fi acquire_lock -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" - detect_hosts # ── Derive key name from hostname ───────────────────────────────────────────────────────────── diff --git a/Plugin/unraid/Partnership/containers.sh b/Plugin/unraid/Partnership/containers.sh index 8806057..bb96403 100755 --- a/Plugin/unraid/Partnership/containers.sh +++ b/Plugin/unraid/Partnership/containers.sh @@ -214,15 +214,16 @@ deploy_xml_stack() { cleanup_deployed_stack_on_remote() { local remote_ip="$1" ssh_key="$2" local -a xml_names=() - [[ ${#PARTNERSHIP_AUTH_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_AUTH_STACK[@]}") - [[ ${#PARTNERSHIP_ARR_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_ARR_STACK[@]}") + [[ ${#PARTNERSHIP_AUTH_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_AUTH_STACK[@]}") + [[ ${#PARTNERSHIP_ARR_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_ARR_STACK[@]}") + [[ ${#PARTNERSHIP_SERVICES_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_SERVICES_STACK[@]}") if [[ ${#xml_names[@]} -eq 0 ]]; then - log "No auth/arr stack arrays configured — skipping deployed stack cleanup" + log "No auth/arr/services stack arrays configured — skipping deployed stack cleanup" return 0 fi - log "Removing owner-deployed containers (auth/arr stacks) from $MIRROR..." + log "Removing owner-deployed containers (auth/arr/services stacks) from $MIRROR..." for xml_name in "${xml_names[@]}"; do [[ -z "$xml_name" ]] && continue local xml_file="${TEMPLATES_DIR}/${xml_name}" @@ -289,15 +290,21 @@ cleanup_deployed_stack_locally() { "source '$SCRIPTS_ROOT/load_config.sh' 2>/dev/null detect_hosts 2>/dev/null printf '%s\n' \"\${PARTNERSHIP_ARR_STACK[@]:-}\"" 2>/dev/null | grep -v '^$') - xml_names=("${auth_arr[@]}" "${arr_arr[@]}") + local -a svc_arr + mapfile -t svc_arr < <(timeout "$SSH_TIMEOUT" ssh -i "$ssh_key" \ + -o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes root@"$owner_ip" \ + "source '$SCRIPTS_ROOT/load_config.sh' 2>/dev/null + detect_hosts 2>/dev/null + printf '%s\n' \"\${PARTNERSHIP_SERVICES_STACK[@]:-}\"" 2>/dev/null | grep -v '^$') + xml_names=("${auth_arr[@]}" "${arr_arr[@]}" "${svc_arr[@]}") fi if [[ ${#xml_names[@]} -eq 0 ]]; then - log "Could not read deployed stack from owner — skipping auth/arr cleanup" + log "Could not read deployed stack from owner — skipping auth/arr/services cleanup" return 0 fi - log "Removing owner-deployed containers (auth/arr stacks) locally..." + log "Removing owner-deployed containers (auth/arr/services stacks) locally..." for xml_name in "${xml_names[@]}"; do [[ -z "$xml_name" ]] && continue local xml_file="${TEMPLATES_DIR}/${xml_name}" @@ -336,3 +343,74 @@ cleanup_deployed_stack_locally() { done } +# ============================================================================================== +# ── Reconfigure a container's WebUI on the remote server ───────────────────────────────────── +# ============================================================================================== +reconfigure_webui() { + local container="$1" port="$2" target_ip="$3" + local ssh_key="$4" remote_ip="$5" label="${6:-remote}" + + log "Reconfiguring $container WebUI → ${target_ip}:${port} on $label..." + + if [[ "$DRY_RUN" == true ]]; then + warn "DRY RUN — would reconfigure $container WebUI to http://${target_ip}:${port}/" + return 0 + fi + + local template + template=$(timeout "$SSH_TIMEOUT" ssh -i "$ssh_key" \ + -o ConnectTimeout="$SSH_TIMEOUT" root@"$remote_ip" \ + "grep -rl '' '$TEMPLATES_DIR/' 2>/dev/null | \ + xargs grep -l '\"$container\"' 2>/dev/null | head -1" 2>/dev/null) + + if [[ -z "$template" ]]; then + warn "$container template not found on $label — WebUI needs manual reconfiguration" + return 1 + fi + + timeout "$SSH_TIMEOUT" ssh -i "$ssh_key" \ + -o ConnectTimeout="$SSH_TIMEOUT" root@"$remote_ip" \ + "sed -i 's|.*|http://${target_ip}:${port}/|g' '$template'" \ + 2>/dev/null && \ + log "$container → http://${target_ip}:${port}/ ✅" || { + error "Failed to reconfigure $container WebUI on $label" + return 1 + } +} + +# ============================================================================================== +# ── Reconfigure local auth WebUIs to target IP ─────────────────────────────────────────────── +# ============================================================================================== +reconfigure_local_webuis() { + local target_ip="$1" + log "Reconfiguring local auth WebUIs → ${target_ip}..." + + local failures=0 + for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]}"; do + [[ -z "$entry" ]] && continue + local container="${entry%%|*}" + local port="${entry##*|}" + + local template + template=$(grep -rl '' "$TEMPLATES_DIR/" 2>/dev/null | \ + xargs grep -l "\"$container\"" 2>/dev/null | head -1) + + if [[ -z "$template" ]]; then + warn "$container template not found locally" + (( failures++ )) + continue + fi + + if [[ "$DRY_RUN" == true ]]; then + warn "DRY RUN — would reconfigure $container → http://${target_ip}:${port}/" + continue + fi + + sed -i "s|.*|http://${target_ip}:${port}/|g" \ + "$template" 2>/dev/null && \ + log "$container → http://${target_ip}:${port}/ ✅" || \ + { error "Failed to reconfigure $container"; (( failures++ )); } + done + return $failures +} + diff --git a/Plugin/unraid/adapter.sh b/Plugin/unraid/adapter.sh index c0765f3..4e043c6 100755 --- a/Plugin/unraid/adapter.sh +++ b/Plugin/unraid/adapter.sh @@ -17,8 +17,9 @@ # ── FUNCTIONS ───────────────────────────────────────────────────────────────────────────────── # platform_require_cmd — verify a platform command exists and is executable # platform_storage_healthy — array mounted and shfs active on /mnt/user -# platform_get_disk_states — raw disks.ini content from emhttp state -# platform_get_temp_thresholds — disk warn/crit °C from dynamix.cfg +# platform_disk_states_path — path to the platform disk state file +# platform_get_disk_states — raw disk state content from platform +# platform_get_temp_thresholds — disk warn/crit °C from platform config # platform_is_maintenance_running — parity check/sync in progress # platform_is_service_enabled — docker or libvirt enabled in boot config # platform_restart_service — restart a named service via rc.d @@ -27,6 +28,14 @@ # platform_is_mover_running — unRAID mover process check # platform_stop_user_scripts — kill all user.scripts background processes # platform_send_os_notification — native unRAID notify (dynamix) +# platform_storage_path — root path for user shares/storage (e.g. /mnt/user) +# platform_webui_install_path — where the platform serves the WebGUI plugin files from +# platform_scripts_dir_probe_cmd — shell command to run on a remote to discover its SCRIPTS_DIR +# platform_get_templates_dir — path to Unraid CA docker templates-user directory +# platform_setup_db_path — path to the persistent Varaverk setup/wizard state database +# platform_rebuild_container — rebuild a container from its stored XML template +# platform_push_conf — push master.conf to all listed hosts via WebGUI PHP +# platform_push_setup_state — push wizard setup state to WebGUI PHP # ============================================================================================== # ────────────────────────────────────────────────────────────────────────────────────────────── @@ -79,23 +88,30 @@ platform_storage_healthy() { # Writes raw /var/local/emhttp/disks.ini to stdout. # Returns 1 if the file is absent (array not started or emhttp not running). # ────────────────────────────────────────────────────────────────────────────────────────────── +platform_disk_states_path() { + echo "/var/local/emhttp/disks.ini" +} + platform_get_disk_states() { - local disks_ini="/var/local/emhttp/disks.ini" + local disks_ini + disks_ini=$(platform_disk_states_path) [[ -f "$disks_ini" ]] || return 1 cat "$disks_ini" } # ────────────────────────────────────────────────────────────────────────────────────────────── # platform_get_temp_thresholds -# Writes two space-separated values to stdout: WARN_TEMP CRIT_TEMP (°C integers). -# Falls back to 45 55 if dynamix.cfg is absent or the keys are missing. +# Writes four space-separated values to stdout: HDD_HOT HDD_MAX SSD_HOT SSD_MAX (°C integers). +# Falls back to 45/55/60/70 if dynamix.cfg is absent or keys are missing. # ────────────────────────────────────────────────────────────────────────────────────────────── platform_get_temp_thresholds() { local cfg="/boot/config/plugins/dynamix/dynamix.cfg" - local warn crit - warn=$(grep -m1 '^diskWarn=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"') - crit=$(grep -m1 '^diskCrit=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"') - echo "${warn:-45} ${crit:-55}" + local hdd_hot hdd_max ssd_hot ssd_max + hdd_hot=$(grep -m1 '^hot=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"') + hdd_max=$(grep -m1 '^max=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"') + ssd_hot=$(grep -m1 '^hotssd=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"') + ssd_max=$(grep -m1 '^maxssd=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"') + echo "${hdd_hot:-45} ${hdd_max:-55} ${ssd_hot:-60} ${ssd_max:-70}" } # ────────────────────────────────────────────────────────────────────────────────────────────── @@ -200,6 +216,39 @@ platform_stop_user_scripts() { # severity: normal | warning | alert (default: normal) # Returns 1 if the notify script is absent. # ────────────────────────────────────────────────────────────────────────────────────────────── +# ── Platform path defaults ───────────────────────────────────────────────────────────────────── +# Set here so master.conf and core scripts never contain OS-specific path literals. +# host*.conf may override any of these after the adapter is sourced. +DOCKER_APPDATA_BASE="${DOCKER_APPDATA_BASE:-/mnt/user/appdata}" + +# ────────────────────────────────────────────────────────────────────────────────────────────── +# platform_storage_path +# Writes the root path where user shares and storage are accessible to stdout. +# Used by array health checks and disk verification. +# ────────────────────────────────────────────────────────────────────────────────────────────── +platform_storage_path() { + echo "/mnt/user" +} + +# ────────────────────────────────────────────────────────────────────────────────────────────── +# platform_webui_install_path +# Writes the path where the platform serves WebGUI plugin files from. +# On Unraid the PHP WebGUI is served from /boot/, separate from SCRIPTS_DIR. +# Returns 1 (empty) if the platform serves directly from SCRIPTS_DIR (no sync needed). +# ────────────────────────────────────────────────────────────────────────────────────────────── +platform_webui_install_path() { + echo "/boot/config/plugins/varaverk" +} + +# ────────────────────────────────────────────────────────────────────────────────────────────── +# platform_scripts_dir_probe_cmd +# Writes a shell command suitable for running on a remote via SSH to discover +# that remote's SCRIPTS_DIR. Each platform exposes its install path differently. +# ────────────────────────────────────────────────────────────────────────────────────────────── +platform_scripts_dir_probe_cmd() { + echo "grep -m1 'SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'" +} + platform_send_os_notification() { local message="$1" local subject="${2:-Varaverk}" @@ -209,3 +258,66 @@ platform_send_os_notification() { [[ -x "$notify_script" ]] || return 1 "$notify_script" -e "Varaverk" -s "$subject" -d "$message" -i "$severity" 2>/dev/null } + +# ────────────────────────────────────────────────────────────────────────────────────────────── +# platform_get_templates_dir +# Writes the path to the Unraid CA docker templates-user directory to stdout. +# ────────────────────────────────────────────────────────────────────────────────────────────── +platform_get_templates_dir() { + echo "/boot/config/plugins/dockerMan/templates-user" +} + +# ────────────────────────────────────────────────────────────────────────────────────────────── +# platform_setup_db_path +# Writes the path to the persistent Varaverk setup/wizard state database. +# ────────────────────────────────────────────────────────────────────────────────────────────── +platform_setup_db_path() { + echo "/boot/config/varaverk_setup.db" +} + +# ────────────────────────────────────────────────────────────────────────────────────────────── +# platform_rebuild_container +# Rebuilds a container from its stored Unraid CA XML template (stops old, recreates on new +# image digest, prunes old image). Returns 1 if the rebuild script is absent or fails. +# ────────────────────────────────────────────────────────────────────────────────────────────── +platform_rebuild_container() { + local container="$1" + local rebuild_script="/usr/local/emhttp/plugins/dynamix.docker.manager/scripts/rebuild_container" + [[ -x "$rebuild_script" ]] || return 1 + "$rebuild_script" "$container" >/dev/null 2>&1 +} + +# ────────────────────────────────────────────────────────────────────────────────────────────── +# platform_push_conf +# Pushes master.conf to all listed hosts via the WebGUI PHP API. +# Writes per-host push results to stdout. Returns 1 if any host failed. +# Returns 0 silently if php is unavailable (caller should warn manually). +# ────────────────────────────────────────────────────────────────────────────────────────────── +platform_push_conf() { + command -v php &>/dev/null || return 0 + local rc=0 + php -r " +require_once '/usr/local/emhttp/plugins/varaverk/include/config.php'; +\$results = vv_push_master_conf(); +if (empty(\$results)) { echo 'no remote hosts'; exit(0); } +\$failed = 0; +foreach (\$results as \$r) { + echo \$r['host'] . ': ' . (\$r['ok'] ? 'pushed' : 'FAILED — ' . \$r['error']) . PHP_EOL; + if (!\$r['ok']) \$failed++; +} +exit(\$failed > 0 ? 1 : 0); +" 2>/dev/null + rc=$? + return $rc +} + +# ────────────────────────────────────────────────────────────────────────────────────────────── +# platform_push_setup_state +# Pushes the Varaverk wizard setup state to all partners via the WebGUI PHP API. +# No-op if php is unavailable. Always returns 0. +# ────────────────────────────────────────────────────────────────────────────────────────────── +platform_push_setup_state() { + command -v php &>/dev/null || return 0 + php -r "require_once '/usr/local/emhttp/plugins/varaverk/include/config.php'; vv_push_setup_state();" 2>/dev/null + return 0 +} diff --git a/Plugin/unraid/include/common.php b/Plugin/unraid/include/common.php index 6c89fec..1caf7a0 100644 --- a/Plugin/unraid/include/common.php +++ b/Plugin/unraid/include/common.php @@ -404,9 +404,13 @@ function vv_parity_status(): array { if (preg_match('/^(\w+)="([^"]*)"/', $line, $m)) $var[$m[1]] = $m[2]; } - $isValid = ($var['mdNumInvalid'] ?? '0') === '0'; - $exitCode = (int)($var['sbSyncExit'] ?? 0); - $errors = (int)($var['sbSyncErrs'] ?? 0); + $numDisabled = (int)($var['mdNumDisabled'] ?? 0); + $numMissing = (int)($var['mdNumMissing'] ?? 0); + $exitCode = (int)($var['sbSyncExit'] ?? 0); + $errors = (int)($var['sbSyncErrs'] ?? 0); + // Emulated (DISK_DSBL) disks are protected by parity and don't make parity invalid. + // True invalidity: sync errors on the last check, or unprotectable missing slots. + $isValid = $errors === 0 && $numMissing === 0; $inProgress = ($var['mdResync'] ?? '0') !== '0'; $resyncPos = (int)($var['mdResyncPos'] ?? 0); $resyncSize = (int)($var['mdResyncSize'] ?? 1); @@ -452,6 +456,8 @@ function vv_parity_status(): array { $exitMap = ['0' => 'Completed', '-4' => 'Aborted', '-5' => 'Cancelled']; return [ 'valid' => $isValid, + 'num_disabled' => $numDisabled, + 'num_missing' => $numMissing, 'in_progress' => $inProgress, 'resync_pct' => $resyncPct, 'exit_code' => $exitCode, diff --git a/Plugin/unraid/pages/monitor.php b/Plugin/unraid/pages/monitor.php index 102a737..47ba3ad 100644 --- a/Plugin/unraid/pages/monitor.php +++ b/Plugin/unraid/pages/monitor.php @@ -561,20 +561,23 @@ function vvDiskRow(disk) { const tempColor = vvTempColor(tempC, disk.transport); const tempStr = tempC !== null ? `${tempC}°` : '—'; const isParity = disk.role === 'parity'; - const nameColor = isParity ? '#6a8faf' : '#aaa'; + const failed = !isParity && disk.status && disk.status !== 'DISK_OK'; + const nameColor = isParity ? '#6a8faf' : failed ? '#f44336' : '#aaa'; const pct = disk.pct ?? 0; const barColor = isParity ? '#1e3a5a' + : failed ? '#f44336' : pct >= vvThresholds.util_crit ? '#f44336' : pct >= vvThresholds.util_warn ? '#ff9800' : '#4caf50'; const barWidth = isParity ? '100' : pct; const spinLabel = (!isParity && !disk.mounted) ? `` : ''; + const failLabel = failed ? `${disk.status === 'DISK_DSBL' ? 'emulated' : disk.status.replace('DISK_','').toLowerCase()}` : ''; const right = isParity ? `${vvFmt(disk.size_gb)}` : `${vvFmt(disk.used_gb)} / ${vvFmt(disk.size_gb)}`; return `
- ${disk.name}${spinLabel}${vvIoChip(disk.device)} + ${disk.name}${spinLabel}${failLabel}${vvIoChip(disk.device)} ${right} ${tempStr}
@@ -968,10 +971,8 @@ function vvPollMonitor() { // ── Parity ────────────────────────────────────────────────────────────── const par = d.parity ?? {}; (function() { - const valid = par.valid !== false; - const inProg = par.in_progress; - const validColor = valid ? '#4caf50' : '#f44336'; - const validLabel = valid ? 'Parity is valid' : 'Parity is INVALID'; + const valid = par.valid !== false; + const inProg = par.in_progress; function vvRelTime(ts) { if (!ts) return ''; @@ -999,15 +1000,21 @@ function vvPollMonitor() { return new Date(ts * 1000).toLocaleString([], {weekday:'short',day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'}); } - const exitColor = par.exit_label === 'Completed' ? '#4caf50' : par.exit_label === 'Aborted' ? '#ff9800' : '#f44336'; - const errColor = (par.errors ?? 0) > 0 ? '#f44336' : '#444'; - const speedStr = par.last_speed_mb ? ` · ${par.last_speed_mb} MB/s` : ''; - const nextDate = vvFmtDate(par.next_ts); - const dueIn = vvDueIn(par.next_ts); - const parBannerCls = !valid ? 'vv-banner-err' : (par.errors ?? 0) > 0 ? 'vv-banner-warn' : 'vv-banner-ok'; + const numDisabled = par.num_disabled ?? 0; + const numMissing = par.num_missing ?? 0; + const degraded = numDisabled > 0 || numMissing > 0; + const exitColor = par.exit_label === 'Completed' ? '#4caf50' : par.exit_label === 'Aborted' ? '#ff9800' : '#f44336'; + const errColor = (par.errors ?? 0) > 0 ? '#f44336' : '#444'; + const speedStr = par.last_speed_mb ? ` · ${par.last_speed_mb} MB/s` : ''; + const nextDate = vvFmtDate(par.next_ts); + const dueIn = vvDueIn(par.next_ts); + const parBannerCls = !valid ? 'vv-banner-err' : degraded || (par.errors ?? 0) > 0 ? 'vv-banner-warn' : 'vv-banner-ok'; + const emulLabel = numDisabled > 0 ? `Emulating ${numDisabled} disk${numDisabled !== 1 ? 's' : ''}` : ''; + const missingLabel = numMissing > 0 ? `${numMissing} slot${numMissing !== 1 ? 's' : ''} missing` : ''; let html = `
${valid ? '✓ Valid' : '✗ INVALID'} + ${emulLabel}${missingLabel} ${(par.errors ?? 0) > 0 ? `${par.errors} error${par.errors !== 1 ? 's' : ''}` : ''}
`; @@ -1027,6 +1034,7 @@ function vvPollMonitor() { ${par.exit_label ?? '—'} Errors ${par.errors ?? 0} + ${numDisabled > 0 ? `Emulating${numDisabled} disk${numDisabled !== 1 ? 's' : ''}` : ''} Next check ${nextDate}
diff --git a/Rsync/rsync.sh b/Rsync/rsync.sh index f7468cf..9b4d9ac 100755 --- a/Rsync/rsync.sh +++ b/Rsync/rsync.sh @@ -170,10 +170,6 @@ if ! command -v docker &>/dev/null; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" detect_hosts diff --git a/System_Essentials/clear_logs.sh b/System_Essentials/clear_logs.sh index 1322856..f3a9a6c 100755 --- a/System_Essentials/clear_logs.sh +++ b/System_Essentials/clear_logs.sh @@ -93,10 +93,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/System_Essentials/conf_sync.sh b/System_Essentials/conf_sync.sh index afbecd8..c24c702 100755 --- a/System_Essentials/conf_sync.sh +++ b/System_Essentials/conf_sync.sh @@ -100,7 +100,7 @@ for host_var in $(compgen -v | grep -E '^HOST[0-9]+$' | sort); do # ── Pull: grab partner's conf from their disk → our local cache ────────── if [[ "$PUSH_ONLY" == false ]]; then - remote_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf" + remote_conf="${SCRIPTS_DIR}/Configurations/${partner_slot}.conf" if [[ "$DRY_RUN" == true ]]; then warn "DRY RUN — would pull $partner_host:$remote_conf → $CACHE_DIR/${partner_slot}.conf" elif timeout "$SSH_TIMEOUT" scp -i "$SSH_KEY" \ diff --git a/System_Essentials/docker_syslog_filter.sh b/System_Essentials/docker_syslog_filter.sh index 34deb05..38af2de 100755 --- a/System_Essentials/docker_syslog_filter.sh +++ b/System_Essentials/docker_syslog_filter.sh @@ -93,10 +93,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/System_Essentials/inotify_tuning.sh b/System_Essentials/inotify_tuning.sh index c34329d..476ea21 100755 --- a/System_Essentials/inotify_tuning.sh +++ b/System_Essentials/inotify_tuning.sh @@ -99,10 +99,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/System_Essentials/rsync_stop.sh b/System_Essentials/rsync_stop.sh index 7b18216..e329cfd 100755 --- a/System_Essentials/rsync_stop.sh +++ b/System_Essentials/rsync_stop.sh @@ -114,10 +114,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/System_Essentials/server_reboot.sh b/System_Essentials/server_reboot.sh index 4d51159..69d9378 100755 --- a/System_Essentials/server_reboot.sh +++ b/System_Essentials/server_reboot.sh @@ -110,10 +110,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" validate_int REBOOT_SLEEP "$REBOOT_SLEEP" diff --git a/Tools/bulk_permissions_repair.sh b/Tools/bulk_permissions_repair.sh index a6e43f0..bcc9815 100755 --- a/Tools/bulk_permissions_repair.sh +++ b/Tools/bulk_permissions_repair.sh @@ -98,10 +98,6 @@ fi acquire_lock -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" # detect_hosts() sets MY_ID — used in summary detect_hosts diff --git a/Tools/container_data_export.sh b/Tools/container_data_export.sh index d0b9ebd..b58f836 100755 --- a/Tools/container_data_export.sh +++ b/Tools/container_data_export.sh @@ -93,10 +93,6 @@ if ! command -v docker &>/dev/null; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" # detect_hosts() sets MY_ID — used in summary detect_hosts diff --git a/Tools/emby_database_repair.sh b/Tools/emby_database_repair.sh index 3a722b3..f7d25cd 100755 --- a/Tools/emby_database_repair.sh +++ b/Tools/emby_database_repair.sh @@ -101,10 +101,6 @@ platform_require_cmd \ "--version" "." \ "sqlite3" || { error "sqlite3 not found — install sqlite package"; exit 1; } -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" if ! command -v jq >/dev/null 2>&1; then error "jq not found — required to detect Emby config path from Docker mounts" diff --git a/Tools/fallback_state_reset.sh b/Tools/fallback_state_reset.sh index 0127061..30bc203 100755 --- a/Tools/fallback_state_reset.sh +++ b/Tools/fallback_state_reset.sh @@ -88,10 +88,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Tools/ramdisk_stop.sh b/Tools/ramdisk_stop.sh index 22fbd9e..474bcb3 100755 --- a/Tools/ramdisk_stop.sh +++ b/Tools/ramdisk_stop.sh @@ -101,10 +101,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Tools/smart_long_test.sh b/Tools/smart_long_test.sh index 856cc0b..8520f17 100755 --- a/Tools/smart_long_test.sh +++ b/Tools/smart_long_test.sh @@ -112,10 +112,6 @@ platform_require_cmd \ exit 1 } -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Tools/watchdog_skip_list_manager.sh b/Tools/watchdog_skip_list_manager.sh index f6d04da..d3d8afd 100755 --- a/Tools/watchdog_skip_list_manager.sh +++ b/Tools/watchdog_skip_list_manager.sh @@ -109,10 +109,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Tools/zfs_pool_scrub.sh b/Tools/zfs_pool_scrub.sh index 1b9a199..5c5e7a9 100755 --- a/Tools/zfs_pool_scrub.sh +++ b/Tools/zfs_pool_scrub.sh @@ -103,10 +103,6 @@ platform_require_cmd \ exit 1 } -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Transcodes/ramdisk_setup.sh b/Transcodes/ramdisk_setup.sh index e4fa9a6..9bf2b3c 100755 --- a/Transcodes/ramdisk_setup.sh +++ b/Transcodes/ramdisk_setup.sh @@ -117,10 +117,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Transcodes/transcode_cleanup.sh b/Transcodes/transcode_cleanup.sh index 78c02f0..659cfc9 100755 --- a/Transcodes/transcode_cleanup.sh +++ b/Transcodes/transcode_cleanup.sh @@ -112,10 +112,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock "wait" diff --git a/Transcodes/transcode_manager.sh b/Transcodes/transcode_manager.sh index 347bb44..2b067de 100755 --- a/Transcodes/transcode_manager.sh +++ b/Transcodes/transcode_manager.sh @@ -150,10 +150,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock "wait" diff --git a/Watchdogs/System/network_watchdog.sh b/Watchdogs/System/network_watchdog.sh index 74d3dbb..3bcf6b6 100755 --- a/Watchdogs/System/network_watchdog.sh +++ b/Watchdogs/System/network_watchdog.sh @@ -112,10 +112,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Watchdogs/docker_watchdog.sh b/Watchdogs/docker_watchdog.sh index fc5e5ac..4b207b9 100755 --- a/Watchdogs/docker_watchdog.sh +++ b/Watchdogs/docker_watchdog.sh @@ -254,12 +254,6 @@ fi log "$ICON_GEAR Config: grace=${WATCHDOG_STARTUP_GRACE}s mem-soft=${SOFT_MEM_THRESHOLD}% cpu-soft=${SOFT_CPU_THRESHOLD}% cpu-hard=${HARD_CPU_THRESHOLD}% cpu-limit=${CPU_FAIL_LIMIT} http-limit=${RESP_FAIL_LIMIT} daemon-timeout=${DOCKER_TIMEOUT}s" log "$ICON_CONTAINERS Tier1: watched=${#WATCHDOG_CONTAINERS[@]} required=${#WATCHDOG_REQUIRED_CONTAINERS[@]} urls=${#WATCHDOG_CONTAINER_URLS[@]} restart-limit=${WATCHDOG_CONTAINER_RESTART_LIMIT}/${WATCHDOG_CONTAINER_RESTART_WINDOW}h" -# Validate unRAID-specific commands used by this script -# If rc.docker is missing or changed, daemon restart will fail — better to know now -platform_require_cmd "/etc/rc.d/rc.docker" "" "" "Docker rc.d script" || warn "rc.docker not found — daemon restart unavailable if needed" - -platform_require_cmd "/usr/local/emhttp/plugins/dynamix/scripts/notify" "" "" "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" - # Ensure state files exist touch "$WATCHDOG_STATE_FILE" "$WATCHDOG_CONTAINER_RESTART_LOG" \ "$DOCKER_WATCHDOG_FAILED_FILE" 2>/dev/null diff --git a/Watchdogs/stability_watchdog.sh b/Watchdogs/stability_watchdog.sh index 3b054cb..dc883b4 100755 --- a/Watchdogs/stability_watchdog.sh +++ b/Watchdogs/stability_watchdog.sh @@ -119,10 +119,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/Watchdogs/system_watchdog.sh b/Watchdogs/system_watchdog.sh index 9fefbe3..f559fb8 100755 --- a/Watchdogs/system_watchdog.sh +++ b/Watchdogs/system_watchdog.sh @@ -45,10 +45,6 @@ if [[ "$EUID" -ne 0 ]]; then exit 1 fi -platform_require_cmd \ - "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ - "" "" \ - "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock diff --git a/common.sh b/common.sh index e0060c0..85bd6ca 100755 --- a/common.sh +++ b/common.sh @@ -228,14 +228,11 @@ notify() { log "$ICON_NOTIFY Sending notification: $subject — $message" - # Guard is built in — scripts do NOT need to validate_unraid_cmd for the notify script. if [[ "${NOTIFY_UNRAID:-false}" == true ]]; then - local notify_script="/usr/local/emhttp/plugins/dynamix/scripts/notify" - if [[ -x "$notify_script" ]]; then - "$notify_script" -s "$subject" -d "$message" -i "$severity" 2>/dev/null - log "$ICON_NOTIFY unRAID notification sent" + if platform_send_os_notification "$message" "$subject" "$severity"; then + log "$ICON_NOTIFY OS notification sent" else - log "$ICON_NOTIFY unRAID notify script not found — skipping" + log "$ICON_NOTIFY OS notify not available — skipping" fi fi @@ -586,6 +583,8 @@ detect_hosts() { _alias_array "PARTNERSHIP_REPLACE_CONTAINERS" _alias_array "PARTNERSHIP_ARR_STACK" _alias_array "PARTNERSHIP_ARR_REPLACE_CONTAINERS" + _alias_array "PARTNERSHIP_SERVICES_STACK" + _alias_array "PARTNERSHIP_SERVICES_REPLACE_CONTAINERS" _alias_array "RW_PAUSE_CONTAINERS" _alias_array "RW_STOP_CONTAINERS" @@ -727,34 +726,35 @@ check_rsync_enabled() { } # ============================================================================================== -# ── UNRAID SERVICE STATE ────────────────────────────────────────────────────────────────────── +# ── PLATFORM SERVICE STATE ──────────────────────────────────────────────────────────────────── # ============================================================================================== -# Reads Unraid config files to check whether Docker and VM Manager are enabled. -# Use these guards before any script that manages containers or VMs. +# Check whether Docker and VM Manager are enabled on this host. +# Delegates to platform_is_service_enabled() from the platform adapter. is_docker_enabled() { - [[ "$(grep -oP '(?<=DOCKER_ENABLED=")[^"]+' /boot/config/docker.cfg 2>/dev/null)" == "yes" ]] + platform_is_service_enabled docker } is_vm_manager_enabled() { - [[ "$(grep -oP '(?<=SERVICE=")[^"]+' /boot/config/domain.cfg 2>/dev/null)" == "enable" ]] + platform_is_service_enabled libvirt } # ============================================================================================== # ── LOCAL HEALTH CHECKS ─────────────────────────────────────────────────────────────────────── # ============================================================================================== -# Verifies local /mnt/user is mounted and has shares. +# Verifies local storage is mounted and has shares. # Non-fatal — returns status for caller to decide. # Used by fallback before starting remote containers locally. check_local_array() { log "Checking local array..." - if ! mountpoint -q /mnt/user 2>/dev/null; then - error "$ICON_DISK Local array is not started — /mnt/user is not mounted" + if ! platform_storage_healthy; then + error "$ICON_DISK Local array is not started — storage not mounted" return 1 fi - local file_count - file_count=$(ls /mnt/user 2>/dev/null | wc -l) + local _storage_path file_count + _storage_path=$(platform_storage_path) + file_count=$(ls "$_storage_path" 2>/dev/null | wc -l) if [[ "$file_count" -eq 0 ]]; then error "$ICON_DISK Local array appears empty — shares may not be available" return 1 @@ -767,14 +767,15 @@ check_local_array() { # ── REMOTE HEALTH CHECKS ───────────────────────────────────────────────────────────────────── # ============================================================================================== -# Verifies remote /mnt/user is mounted via SSH. +# Verifies remote storage is mounted via SSH. # Non-fatal — returns status. Used before handback rsync. # Syncing to remote with no array fills rootfs rapidly. check_remote_array() { log "Checking remote array on $REMOTE_SERVER_NAME..." - local result + local _spath result + _spath=$(platform_storage_path) result=$(ssh -i "$SSH_KEY" -o ConnectTimeout=10 root@"$REMOTE_SERVER" \ - "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null) + "mountpoint -q '$_spath' && echo yes || echo no" 2>/dev/null) if [[ "$result" != "yes" ]]; then error "$ICON_DISK Remote array not started on $REMOTE_SERVER_NAME" return 1 @@ -853,18 +854,11 @@ is_ssd() { [[ "$(cat "/sys/block/$base/queue/rotational" 2>/dev/null)" == "0" ]] } -# Reads disk temperature thresholds from unRAID's dynamix.cfg. # Sets globals: UNRAID_DISK_HOT UNRAID_DISK_MAX UNRAID_SSD_HOT UNRAID_SSD_MAX -# Falls back to values in master.conf (SMART_TEMP_WARN/CRIT) if file not found. +# Falls back to master.conf values (SMART_TEMP_WARN/CRIT) if platform returns defaults. get_unraid_temp_thresholds() { - local cfg="/boot/config/plugins/dynamix/dynamix.cfg" - if [[ -f "$cfg" ]]; then - UNRAID_DISK_HOT=$(grep '^hot=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"') - UNRAID_DISK_MAX=$(grep '^max=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"') - UNRAID_SSD_HOT=$(grep '^hotssd=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"') - UNRAID_SSD_MAX=$(grep '^maxssd=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"') - fi - # Fall back to master.conf values if dynamix.cfg not found or values empty + read -r UNRAID_DISK_HOT UNRAID_DISK_MAX UNRAID_SSD_HOT UNRAID_SSD_MAX \ + <<< "$(platform_get_temp_thresholds)" UNRAID_DISK_HOT="${UNRAID_DISK_HOT:-${SMART_TEMP_WARN:-45}}" UNRAID_DISK_MAX="${UNRAID_DISK_MAX:-${SMART_TEMP_CRIT:-55}}" UNRAID_SSD_HOT="${UNRAID_SSD_HOT:-60}" @@ -884,10 +878,10 @@ get_unraid_temp_thresholds() { check_local_disk_temps() { get_unraid_temp_thresholds - local disks_ini="/var/local/emhttp/disks.ini" - if [[ ! -f "$disks_ini" ]]; then - warn "disks.ini not found — skipping temp check" - TEMP_CHECK_RESULT="temp check skipped (disks.ini not found)" + local disks_ini_content + if ! disks_ini_content=$(platform_get_disk_states); then + warn "disk state unavailable — skipping temp check" + TEMP_CHECK_RESULT="temp check skipped (disk state unavailable)" return 0 fi @@ -937,7 +931,7 @@ check_local_disk_temps() { current_temp="${current_temp//[^0-9]/}" current_temp="${current_temp:-0}" fi - done < "$disks_ini" + done <<< "$disks_ini_content" check_drive # process last drive if [[ ${#crit_drives[@]} -gt 0 ]]; then @@ -980,13 +974,14 @@ check_remote_disks() { info "$ICON_DISK Checking disks backing $share_name on $REMOTE_SERVER_NAME..." - # Read remote disks.ini for fsType mapping - local disks_ini_content + # Read remote disk state for fsType mapping + local disks_ini_content _disk_states_path + _disk_states_path=$(platform_disk_states_path) disks_ini_content=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \ - "cat /var/local/emhttp/disks.ini 2>/dev/null" 2>/dev/null) + "cat '$_disk_states_path' 2>/dev/null" 2>/dev/null) if [[ -z "$disks_ini_content" ]]; then - error "$ICON_DISK Cannot read disks.ini from $REMOTE_SERVER_NAME" + error "$ICON_DISK Cannot read disk state from $REMOTE_SERVER_NAME" exit 1 fi @@ -1004,11 +999,13 @@ check_remote_disks() { grep -q . && echo \"\$pool\" done | sort -u" 2>/dev/null) - # Tier 3 — shfs fallback via /mnt/user + # Tier 3 — storage root fallback + local _spath + _spath=$(platform_storage_path) if [[ -z "$backing_disks" ]] && [[ -z "$zfs_pool_paths" ]]; then local on_user on_user=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \ - "find /mnt/user -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \ + "find '$_spath' -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \ 2>/dev/null) if [[ -n "$on_user" ]]; then local actual_path @@ -1020,12 +1017,12 @@ check_remote_disks() { zfs_pool_paths="$pool_name" info "$ICON_DISK $share_name found at $actual_path on $REMOTE_SERVER_NAME" else - # Can't determine pool — verify shfs itself is mounted + # Can't determine pool — verify storage root itself is mounted local shfs_ok shfs_ok=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \ - "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null) + "mountpoint -q '$_spath' && echo yes || echo no" 2>/dev/null) if [[ "$shfs_ok" == "yes" ]]; then - success "All disks backing $share_name are online ✅ (via shfs)" + success "All disks backing $share_name are online ✅ (via storage root)" return 0 fi fi diff --git a/git_pull_execute.sh b/git_pull_execute.sh index a944103..8c99c3f 100755 --- a/git_pull_execute.sh +++ b/git_pull_execute.sh @@ -255,7 +255,7 @@ else # ── Flash mode: sync Plugin/ to /boot/ so the webUI picks up updates ───── # In flash mode SCRIPTS_DIR is in appdata — Plugin/ lives in the repo there # but Unraid serves PHP from /boot/. Sync after every pull to keep them in step. - _BOOT_DIR="/boot/config/plugins/varaverk" + _BOOT_DIR=$(platform_webui_install_path) if [[ "$TARGET_DIR" != "$_BOOT_DIR" ]]; then echo "" echo "━━━ $ICON_SYNC Flash mode: sync Plugin/ → /boot/ ━━━"