#!/bin/bash set -e # Log function log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [MAIN] $*" } # Array to store background process PIDs declare -a SERVICE_PIDS=() # Function to extract service name from script filename get_service_name() { local script_name="$1" # Remove -start.sh suffix and convert to uppercase for env var echo "$script_name" | sed 's/-start\.sh$//' | tr '[:lower:]' '[:upper:]' | tr '-' '_' } # Define start directory path start_dir="script/start" # Function to start persistence service synchronously (BLOCKING) start_persistence_service() { local persistence_script="$start_dir/persistence-start.sh" if [[ -f "$persistence_script" ]] && [[ "${PERSISTENCE_ENABLED:-false}" == "true" ]]; then log "=== STARTING PERSISTENCE SERVICE (SYNCHRONOUS) ===" log "This is a BLOCKING operation that must complete before other services start" if "$persistence_script"; then log "✓ Persistence service completed successfully" log "Data restoration and verification completed" return 0 else log "✗ Persistence service failed" log "ERROR: Cannot proceed with other services due to persistence failure" exit 1 fi else if [[ "${PERSISTENCE_ENABLED:-false}" == "true" ]]; then log "WARNING: Persistence enabled but script not found: $persistence_script" else log "Persistence service is disabled" fi return 0 fi } # Function to perform restic auto-restore BEFORE any services start perform_restic_restore() { log "=== CHECKING FOR RESTIC AUTO-RESTORE ===" # Prepare restic environment first local user_home="${HOME:-/home/user}" local config_file="$user_home/config/restic.conf" # Load restic configuration BEFORE checking RESTIC_AUTO_RESTORE if [[ -f "$config_file" ]]; then log "Loading restic config from: $config_file" # Load config with environment variable precedence local temp_config="/tmp/restic_config_$$" sed 's/^\([^#]*\)=\(.*\)$/\1=${\1:-\2}/' "$config_file" > "$temp_config" source "$temp_config" rm -f "$temp_config" log "Configuration loaded successfully" else log "No restic configuration file found: $config_file" fi # Now check if restic auto-restore is enabled (after loading config) if [[ "${RESTIC_AUTO_RESTORE:-false}" != "true" ]]; then log "Restic auto-restore is disabled" return 0 fi log "Restic auto-restore is enabled - performing data restoration BEFORE services start" # Set up restic environment export RESTIC_REPOSITORY="${RESTIC_REPOSITORY_PATH:-}" export RESTIC_PASSWORD="${RESTIC_PASSWORD:-}" export RESTIC_CACHE_DIR="${RESTIC_CACHE_DIR:-$user_home/.cache/restic}" # Check if restic is available if ! command -v restic &> /dev/null; then log "WARNING: Restic command not found, skipping auto-restore" log "Install restic to enable backup/restore functionality" return 0 fi # Check if repository is accessible if ! restic snapshots > /dev/null 2>&1; then log "WARNING: Cannot access restic repository, skipping auto-restore" log "This might be the first startup or repository is not yet initialized" log "To create a repository, set RESTIC_ENABLED=true and restart the service" return 0 fi # Check if we have snapshots available - simplified to just check if any exist if ! restic snapshots --json | jq -e '. | length > 0' > /dev/null 2>&1; then log "No snapshots available for restore, skipping auto-restore" return 0 fi # Use the restore script for proper restoration with latest snapshot local restore_script="script/utils/restic-restore.sh" if [[ -x "$restore_script" ]]; then log "Using restic restore script for data restoration with latest snapshot" # Simplified arguments - always use latest snapshot local args=("--mode" "${RESTIC_RESTORE_MODE:-safe}" "--force") # Set restore target path local restore_target if [[ "${RESTIC_RESTORE_MODE:-safe}" == "safe" ]]; then restore_target="${RESTIC_RESTORE_TARGET:-/tmp/restic_restore_$(date +%s)}" else # For replace/direct mode, use parent directory of backup paths as target local backup_parent backup_parent=$(dirname "${RESTIC_BACKUP_PATHS:-/home/user}") restore_target="${RESTIC_RESTORE_TARGET:-$backup_parent}" fi [[ -n "$restore_target" ]] && args+=("--target" "$restore_target") # Add include paths if specified if [[ -n "${RESTIC_RESTORE_INCLUDE_PATHS:-}" ]]; then IFS=':' read -ra paths <<< "$RESTIC_RESTORE_INCLUDE_PATHS" for path in "${paths[@]}"; do args+=("--include" "$path") done fi # Always use latest - no need to pass snapshot ID log "Executing restic restore: $restore_script ${args[*]} (using latest snapshot)" if "$restore_script" "${args[@]}"; then log "✓ Restic auto-restore completed successfully" log "Data restoration finished - services can now start safely" # For safe mode, provide guidance on next steps if [[ "${RESTIC_RESTORE_MODE:-safe}" == "safe" ]]; then log "Safe mode: Data restored to temporary location: $restore_target" log "Please verify and manually copy data if needed" fi return 0 else log "✗ Restic auto-restore failed" return 1 fi else log "ERROR: Restic restore script not found: $restore_script" return 1 fi } # Function to start optional services automatically (AFTER persistence) start_optional_services() { log "Auto-discovering and starting optional services..." # Process all start scripts except core service and persistence for script in "$start_dir"/*-start.sh; do if [[ -f "$script" ]]; then local script_name=$(basename "$script") # Skip core service (handled separately) and persistence (handled above) if [[ "$script_name" == "core-service-start.sh" || "$script_name" == "persistence-start.sh" ]]; then continue fi # Extract service name and construct environment variable name local service_name=$(echo "$script_name" | sed 's/-start\.sh$//') local env_var_name=$(get_service_name "$script_name")_ENABLED # Get the environment variable value (indirect variable expansion) local is_enabled="${!env_var_name:-false}" log "Processing service: $service_name (env: $env_var_name, enabled: $is_enabled)" if [[ "$is_enabled" == "true" ]]; then log "Starting $service_name..." "$script" & local pid=$! SERVICE_PIDS+=($pid) log "$service_name started with PID: $pid" else log "$service_name is disabled" fi fi done if [[ ${#SERVICE_PIDS[@]} -gt 0 ]]; then log "Started ${#SERVICE_PIDS[@]} optional services with PIDs: ${SERVICE_PIDS[*]}" else log "No optional services were started" fi } # Check for backup service conflicts (persistence vs restic) check_backup_service_conflicts() { if [[ "${PERSISTENCE_ENABLED:-false}" == "true" && "${RESTIC_ENABLED:-false}" == "true" ]]; then log "ERROR: Cannot enable both persistence and restic services simultaneously!" log "ERROR: Please choose one backup service:" log "ERROR: - Use persistence: export PERSISTENCE_ENABLED=true RESTIC_ENABLED=false" log "ERROR: - Use restic: export PERSISTENCE_ENABLED=false RESTIC_ENABLED=true" exit 1 fi } # Load service configurations to ensure consistency with actual startup behavior load_service_configs() { # Load restic config if exists (same logic as in perform_restic_restore) local user_home="${HOME:-/home/user}" local restic_config="$user_home/config/restic.conf" if [[ -f "$restic_config" ]]; then local temp_config="/tmp/restic_config_display_$$" sed 's/^\([^#]*\)=\(.*\)$/\1=${\1:-\2}/' "$restic_config" > "$temp_config" source "$temp_config" rm -f "$temp_config" fi # Load persistence config if exists local persistence_config="$user_home/config/persistence.conf" if [[ -f "$persistence_config" ]]; then local temp_config="/tmp/persistence_config_display_$$" sed 's/^\([^#]*\)=\(.*\)$/\1=${\1:-\2}/' "$persistence_config" > "$temp_config" source "$temp_config" rm -f "$temp_config" fi } # Load service configurations first load_service_configs # Display service status summary log "Service configuration:" for script in "$start_dir"/*-start.sh; do if [[ -f "$script" ]]; then script_name=$(basename "$script") if [[ "$script_name" != "core-service-start.sh" ]]; then service_name=$(echo "$script_name" | sed 's/-start\.sh$//') env_var_name=$(get_service_name "$script_name")_ENABLED is_enabled="${!env_var_name:-false}" log " $(echo "$service_name" | sed 's/\b\w/\U&/g'): $is_enabled" fi fi done # Check for backup service conflicts BEFORE starting any services log "Checking for backup service conflicts..." check_backup_service_conflicts # PHASE 1: Start persistence service SYNCHRONOUSLY (BLOCKING) log "=== PHASE 1: PERSISTENCE AND DATA RESTORATION (SYNCHRONOUS) ===" start_persistence_service # Perform restic auto-restore BEFORE any other services start perform_restic_restore || { log "ERROR: Data restoration failed - cannot proceed with service startup" exit 1 } # PHASE 2: Start optional services (AFTER persistence completes) log "=== PHASE 2: OPTIONAL SERVICES ===" log "Starting optional services (persistence completed)..." start_optional_services # PHASE 3: Start core service (required, must be last) log "=== PHASE 3: CORE SERVICE ===" log "Starting core service (all dependencies ready)..." if [[ -f "$start_dir/core-service-start.sh" ]]; then log "Found core service startup script, executing..." exec "$start_dir/core-service-start.sh" else log "ERROR: Core service startup script not found: $start_dir/core-service-start.sh" log "Please ensure you have implemented your core service according to the template documentation." log "See docs/DEVELOPMENT.md for instructions on implementing your core service." exit 1 fi