#!/bin/bash # ============================================================================================== # ================================= Array Start Orchestrator =================================== # ============================================================================================== # Single entry point for "At Startup of Array" in the User Scripts plugin. # Launches everything configured in ARRAY_START_SCRIPTS in master.conf. # This script exits after launching all scripts — unRAID sees it complete normally. # # ── WHAT IT LAUNCHES ────────────────────────────────────────────────────────────────────────── # Configured in master.conf ARRAY_START_SCRIPTS — no changes to this script ever needed. # Current order (order matters — see below): # # ONE-SHOT (run and exit naturally): # unRAID_Essentials/inotify_tuning.sh — raise inotify limits before containers start # unRAID_Essentials/docker_syslog_filter.sh — suppress veth log noise before logs fill # unRAID_Essentials/php_fpm_max_children.sh — WebGUI performance tuning # Transcodes/ramdisk_setup.sh — create tmpfs + symlink before Emby starts # Docker_Essentials/docker_network_connect.sh — ensure networks + container connections # # CONTINUOUS (run until array stops): # unRAID_Essentials/system_watchdog.sh — system health monitor (last line of defense) # Docker_Essentials/docker_watchdog.sh — container health monitor # Failover/failover.sh — mutual failover monitor # # ── WHY ORDER MATTERS ───────────────────────────────────────────────────────────────────────── # inotify_tuning.sh — must run BEFORE Code-Server and other containers start # containers that start with low inotify limits keep them ✅ # docker_syslog_filter — must run BEFORE any container starts creating veth interfaces # ramdisk_setup.sh — must run BEFORE Emby starts transcoding # docker_network_connect — must run BEFORE watchdogs check container states # system_watchdog.sh — before docker_watchdog (system > container priority) # docker_watchdog.sh — before failover (containers must be healthy for failover) # failover.sh — last — needs everything else stable to make decisions # # ── ONE-SHOT vs CONTINUOUS DETECTION ───────────────────────────────────────────────────────── # Script is launched in background with bash script.sh & # After 1 second: if PID still alive → continuous (running in background) # if PID dead + exit 0 → one-shot completed successfully # if PID dead + exit N → failure # # ── SAFEGUARDS ──────────────────────────────────────────────────────────────────────────────── # Root check — all launched scripts require root # acquire_lock — prevents duplicate array start launches # detect_hosts() — MY_ID in notifications # validate_unraid_cmd — notify validated before use # chmod +x auto-fix — non-executable scripts fixed before launch # Full path on failure — shows exact path for debugging # notify on failures — alert if any script fails to launch # # ── CONFIGURATION (master.conf) ─────────────────────────────────────────────────────────────── # ARRAY_START_SCRIPTS — ordered list of scripts to launch at array start # # ── USAGE ───────────────────────────────────────────────────────────────────────────────────── # array_start.sh — normal launch (called by User Scripts at array start) # array_start.sh --dry-run — show what would be launched without launching # array_start.sh --status — show configured scripts and their current state # array_start.sh --log — verbose output per script # ============================================================================================== SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ECOSYSTEM_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" source "$ECOSYSTEM_ROOT/load_config.sh" parse_args "$@" # ============================================================================================== # ━━━ Setup ━━━ # ============================================================================================== if [[ "$EUID" -ne 0 ]]; then error "Must be run as root" exit 1 fi validate_unraid_cmd \ "/usr/local/emhttp/plugins/dynamix/scripts/notify" \ "" "" \ "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled" acquire_lock detect_hosts [[ "$DRY_RUN" == true ]] && warn "DRY RUN — scripts will not be launched" # ============================================================================================== # ━━━ Status ━━━ # ============================================================================================== if [[ "$SHOW_STATUS" == true ]]; then echo "" echo "━━━━━ $ICON_SUMMARY ARRAY START STATUS ━━━━━" echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)" echo "$ICON_GEAR Scripts: ${#ARRAY_START_SCRIPTS[@]} configured" echo "" for relative_path in "${ARRAY_START_SCRIPTS[@]}"; do [[ -z "$relative_path" ]] && continue script_path="$ECOSYSTEM_ROOT/$relative_path" script_name=$(basename "$script_path") if [[ ! -f "$script_path" ]]; then echo " $ICON_ERROR $script_name — FILE NOT FOUND" echo " $script_path" continue fi [[ ! -x "$script_path" ]] && flag=" (not executable — will auto-fix)" || flag="" # Check if currently running if pgrep -f "$script_path" >/dev/null 2>&1; then RUN_PID=$(pgrep -f "$script_path" | head -1) echo " $ICON_RUNNING $script_name — RUNNING (PID $RUN_PID)${flag}" else echo " $ICON_NOT_RUNNING $script_name — not running${flag}" fi done echo "━━━━━━━━━━━━━━━━━━━━━━━" exit 0 fi # ============================================================================================== # ━━━ Launch Scripts ━━━ # ============================================================================================== echo "" echo "━━━ $ICON_GEAR Array Start — $MY_ID — $(date '+%Y-%m-%d %H:%M:%S') ━━━" log "Ecosystem root: $ECOSYSTEM_ROOT" log "Launching ${#ARRAY_START_SCRIPTS[@]} script(s)..." echo "" START=$(date +%s) LAUNCHED=0 FAILED=0 FAILED_SCRIPTS=() for relative_path in "${ARRAY_START_SCRIPTS[@]}"; do [[ -z "$relative_path" ]] && continue SCRIPT_PATH="$ECOSYSTEM_ROOT/$relative_path" SCRIPT_NAME=$(basename "$SCRIPT_PATH") # File existence check if [[ ! -f "$SCRIPT_PATH" ]]; then error "$SCRIPT_NAME — not found" error " Expected: $SCRIPT_PATH" (( FAILED++ )) FAILED_SCRIPTS+=("$SCRIPT_NAME") continue fi # Auto-fix permissions — chmod +x if needed if [[ ! -x "$SCRIPT_PATH" ]]; then warn "$SCRIPT_NAME — not executable, fixing..." chmod +x "$SCRIPT_PATH" || { error "$SCRIPT_NAME — chmod +x failed" (( FAILED++ )) FAILED_SCRIPTS+=("$SCRIPT_NAME") continue } fi if [[ "$DRY_RUN" == true ]]; then warn "DRY RUN — would launch: $SCRIPT_NAME" (( LAUNCHED++ )) continue fi log "$ICON_START Launching $SCRIPT_NAME..." bash "$SCRIPT_PATH" & PID=$! # Brief settle — 1s enough to detect immediate failures sleep 1 if kill -0 "$PID" 2>/dev/null; then # Still running → continuous script warn "$SCRIPT_NAME — running (PID $PID) ✅" (( LAUNCHED++ )) else # Exited — check if one-shot success or failure wait "$PID" EXIT_CODE=$? if [[ "$EXIT_CODE" -eq 0 ]]; then log "$SCRIPT_NAME — completed (one-shot) ✅" (( LAUNCHED++ )) else error "$SCRIPT_NAME — exited with code $EXIT_CODE" error " Path: $SCRIPT_PATH" (( FAILED++ )) FAILED_SCRIPTS+=("$SCRIPT_NAME") fi fi done END=$(date +%s) # ============================================================================================== # ━━━ Summary ━━━ # ============================================================================================== echo "" echo "━━━━━ $ICON_SUMMARY ARRAY START SUMMARY ━━━━━" echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)" echo "$ICON_SUCCESS Launched: $LAUNCHED" [[ "$FAILED" -gt 0 ]] && echo "$ICON_ERROR Failed: $FAILED — ${FAILED_SCRIPTS[*]}" echo "$ICON_TIME Duration: $(format_duration $(( END - START )))" echo "" if [[ "$DRY_RUN" == true ]]; then warn "DRY RUN — no scripts launched" elif [[ "$FAILED" -gt 0 ]]; then warn "Status: $FAILED script(s) failed — ${FAILED_SCRIPTS[*]}" notify "Array start on $(hostname) ($MY_ID) — $FAILED script(s) failed: ${FAILED_SCRIPTS[*]}" \ "Array Start" "warning" else log "$ICON_DONE Status: all $LAUNCHED script(s) launched ✅" fi echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"