diff --git a/rootfs/usr/local/bin/entrypoint.sh b/rootfs/usr/local/bin/entrypoint.sh index 7f11aa5..718628a 100755 --- a/rootfs/usr/local/bin/entrypoint.sh +++ b/rootfs/usr/local/bin/entrypoint.sh @@ -93,7 +93,7 @@ DATABASE_DIR="" # set database dir # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Healthcheck variables HEALTH_ENABLED="yes" # enable healthcheck [yes/no] -SERVICES_LIST="tini" # comma seperated list of processes for the healthcheck +SERVICES_LIST="" # auto-detected from init.d scripts - can be overridden HEALTH_ENDPOINTS="" # url endpoints: [http://localhost/health,http://localhost/test] # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Update path var @@ -341,7 +341,14 @@ if [ "$ENTRYPOINT_FIRST_RUN" != "no" ]; then fi # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # if no pid assume container restart -[ -f "$ENTRYPOINT_PID_FILE" ] && [ -f "/run/__start_init_scripts.pid" ] || START_SERVICES="yes" +# if no pid assume container restart - clean stale files on restart +if [ ! -f "$ENTRYPOINT_PID_FILE" ]; then + START_SERVICES="yes" + # Clean stale pid files from previous container runs + rm -f /run/__start_init_scripts.pid /run/init.d/*.pid /run/*.pid +elif [ ! -f "/run/__start_init_scripts.pid" ]; then + START_SERVICES="yes" +fi # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - [ "$ENTRYPOINT_MESSAGE" = "yes" ] && __printf_space "40" "Container ip address is:" "$CONTAINER_IP4_ADDRESS" # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/rootfs/usr/local/etc/docker/functions/entrypoint.sh b/rootfs/usr/local/etc/docker/functions/entrypoint.sh index 64bb465..dc1c22c 100644 --- a/rootfs/usr/local/etc/docker/functions/entrypoint.sh +++ b/rootfs/usr/local/etc/docker/functions/entrypoint.sh @@ -70,7 +70,108 @@ __clean_variables() { printf '%s' "$var" | grep -v '^$' } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -__no_exit() { [ -f "/run/no_exit.pid" ] || exec bash -c "trap 'sleep 1;rm -Rf /run/*;/tmp/*;/data/logs/start.log;exit 0' TERM INT;(while true; do echo $$ >/run/no_exit.pid;tail -qf /data/logs/start.log 2>/dev/null||sleep 20; done) & wait"; } +# Auto-detect services from init.d scripts +__auto_detect_services() { + local discovered_services="tini" # Always include tini as init + local init_dir="/usr/local/etc/docker/init.d" + + if [ -d "$init_dir" ]; then + for script in "$init_dir"/*.sh; do + if [ -f "$script" ]; then + # Extract service name from filename (remove number prefix and .sh suffix) + local service=$(basename "$script" | sed 's/^[0-9]*-//;s|\.sh$||g') + discovered_services="$discovered_services,$service" + fi + done + fi + + echo "$discovered_services" +} + +# Enhanced __no_exit function with service monitoring and proper failure handling +__no_exit() { + local monitor_services="${SERVICES_LIST:-$(__auto_detect_services)}" + local check_interval="${SERVICE_CHECK_INTERVAL:-30}" + local max_failures="${MAX_SERVICE_FAILURES:-3}" + declare -A failure_counts + + # Initialize failure counters + IFS=',' read -ra services <<< "$monitor_services" + for service in "${services[@]}"; do + service="${service// /}" # trim whitespace + [ -n "$service" ] && failure_counts["$service"]=0 + done + + echo "šŸ” Starting service supervisor - monitoring: $monitor_services" + echo "ā° Check interval: ${check_interval}s, Max failures: $max_failures per service" + + # Set up trap to handle termination gracefully + trap 'echo "šŸ›‘ Container terminating - cleaning up services"; kill $(jobs -p) 2>/dev/null; rm -f /run/*.pid /run/init.d/*.pid; exit 0' TERM INT EXIT + + # Main supervision loop + while true; do + local failed_services="" + local running_services="" + local critical_failure=false + + # Check each monitored service + IFS=',' read -ra services <<< "$monitor_services" + for service in "${services[@]}"; do + service="${service// /}" # trim whitespace + [ -z "$service" ] && continue + + if __pgrep "$service" >/dev/null 2>&1; then + running_services="$running_services $service" + failure_counts["$service"]=0 # reset failure count on success + else + failed_services="$failed_services $service" + failure_counts["$service"]=$((${failure_counts["$service"]:-0} + 1)) + + echo "āš ļø Service '$service' not running (failure ${failure_counts["$service"]}/$max_failures)" + + # Check if we've exceeded max failures for this service + if [ ${failure_counts["$service"]} -ge $max_failures ]; then + echo "šŸ’„ Service '$service' failed $max_failures times - this is critical!" + critical_failure=true + fi + fi + done + + # If we have critical failures, terminate the container + if [ "$critical_failure" = true ]; then + echo "🚨 Critical service failure detected:" + echo " šŸ’€ Dead services: $failed_services" + echo " āœ… Running services: $running_services" + echo " šŸ”„ Container will terminate to allow restart by orchestrator" + + # Write final status to log + { + echo "$(date): CRITICAL FAILURE - Container terminating" + echo "Dead services: $failed_services" + echo "Running services: $running_services" + } >> "/data/logs/start.log" + + # Terminate the container (PID 1 is the init process) + kill -TERM 1 + exit 1 + fi + + # Log status periodically (every 10 cycles = ~5 minutes with 30s interval) + if [ $(($(date +%s) % 300)) -lt $check_interval ]; then + echo "šŸ“Š Service status - Running:$running_services Failed:$failed_services" + # Write to start.log for backward compatibility + echo "$(date): Services running:$running_services failed:$failed_services" >> "/data/logs/start.log" + fi + + sleep "$check_interval" + done & + + # Keep the original behavior for log tailing (for compatibility) + [ -f "/data/logs/start.log" ] && tail -f "/data/logs/start.log" >/dev/null 2>&1 & + + # Wait for background processes + wait +} # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - __trim() { local var="${*//;/ }" @@ -642,46 +743,130 @@ __exec_command() { # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Setup the server init scripts __start_init_scripts() { + set -e + trap 'echo "āŒ Fatal error in service startup - killing container"; rm -f /run/__start_init_scripts.pid; kill -TERM 1' ERR + [ "$1" = " " ] && shift 1 [ "$DEBUGGER" = "on" ] && echo "Enabling debugging" && set -o pipefail -x$DEBUGGER_OPTIONS || set -o pipefail - local retPID="" + local basename="" local init_pids="" local retstatus="0" local initStatus="0" + local failed_services="" + local successful_services="" local init_dir="${1:-/usr/local/etc/docker/init.d}" - local init_count="$(ls -A "$init_dir"/* 2>/dev/null | grep -v '\.sample' | wc -l)" - touch /run/__start_init_scripts.pid - mkdir -p "/tmp" "/run" "/run/init.d" "/usr/local/etc/docker/exec" - chmod -R 777 "/tmp" "/run" "/run/init.d" "/usr/local/etc/docker/exec" + local init_count="$(find "$init_dir" -name "*.sh" 2>/dev/null | wc -l)" + + if [ -n "$SERVICE_DISABLED" ]; then + echo "$SERVICE_DISABLED is disabled" + unset SERVICE_DISABLED + return 0 + fi + + echo "šŸš€ Starting container services initialization" + echo "šŸ“‚ Init directory: $init_dir" + echo "šŸ“Š Services to start: $init_count" + + # Create a fresh PID file to track this startup session + echo $$ > /run/__start_init_scripts.pid + + mkdir -p "/tmp" "/run" "/run/init.d" "/usr/local/etc/docker/exec" "/data/logs/init" + chmod -R 777 "/tmp" "/run" "/run/init.d" "/usr/local/etc/docker/exec" "/data/logs/init" + if [ "$init_count" -eq 0 ] || [ ! -d "$init_dir" ]; then - mkdir -p "/data/logs/init" - while :; do echo "Running: $(date)" >"/data/logs/init/keep_alive" && sleep 3600; done & + echo "āš ļø No init scripts found in $init_dir" + # Still create a minimal keep-alive for containers without services + while true; do + echo "$(date): No services - container keep-alive" >> "/data/logs/start.log" + sleep 3600 + done & else + echo "šŸ“‹ Found $init_count service scripts to execute" + if [ -d "$init_dir" ]; then - [ -f "$init_dir/service.sample" ] && __rm "$init_dir"/*.sample - chmod -Rf 755 "$init_dir"/*.sh + # Remove sample files + find "$init_dir" -name "*.sample" -delete 2>/dev/null + + # Make scripts executable + find "$init_dir" -name "*.sh" -exec chmod 755 {} \; 2>/dev/null + + # Execute scripts in numerical/alphabetical order for init in "$init_dir"/*.sh; do if [ -x "$init" ]; then - name="$(basename "$init")" - service="$(printf '%s' "$name" | sed 's/^[^-]*-//;s|.sh$||g')" - printf '# - - - executing file: %s\n' "$init" - eval "$init" && sleep 5 || sleep 3 - retPID=$(__get_pid "$service") - if [ -n "$retPID" ]; then - initStatus="0" - printf '# - - - %s has been started - pid: %s\n' "$service" "${retPID:-error}" + basename="$(basename "$init")" + service="$(printf '%s' "$basename" | sed 's/^[0-9]*-//;s|\.sh$||g')" + + printf '\nšŸ”§ Executing service script: %s (service: %s)\n' "$init" "$service" + + # Execute the init script and capture its exit code + if eval "$init"; then + sleep 5 # Give service more time to start properly + + # Verify the service actually started by checking for PID + retPID=$(__get_pid "$service") + if [ -n "$retPID" ]; then + initStatus="0" + successful_services="$successful_services $service" + printf 'āœ… Service %s started successfully - PID: %s\n' "$service" "$retPID" + else + # Service script succeeded but no PID found - this is suspicious + initStatus="1" + failed_services="$failed_services $service" + printf 'āš ļø Service %s script completed but no PID found\n' "$service" + fi else - initStatus="1" - printf '# - - - %s has falied to start - check log %s\n' "$service" "docker log $CONTAINER_NAME" + # Service script failed + script_exit_code="$?" + initStatus="1" + failed_services="$failed_services $service" + printf 'āŒ Init script %s failed with exit code %s\n' "$init" "$script_exit_code" fi - echo "" + else + printf 'āš ļø Script %s is not executable, skipping\n' "$init" fi + retstatus=$(($retstatus + $initStatus)) + printf '\n' done + + printf 'šŸ“Š Service startup summary:\n' + printf ' āœ… Successful: %s\n' "${successful_services:-none}" + printf ' āŒ Failed: %s\n' "${failed_services:-none}" + printf ' šŸ“ˆ Total status code: %s\n' "$retstatus" + + # If any services failed to start, terminate the container immediately + if [ $retstatus -gt 0 ]; then + echo "" + echo "šŸ’„ Service startup failures detected!" + echo "šŸ”„ Container will terminate to allow orchestrator restart" + echo "šŸ“ Check container logs for detailed failure information" + + # Write failure information to log + { + echo "$(date): SERVICE STARTUP FAILURE" + echo "Successful services: $successful_services" + echo "Failed services: $failed_services" + echo "Total errors: $retstatus" + } >> "/data/logs/start.log" + + # Clean up and exit + rm -f /run/__start_init_scripts.pid + exit $retstatus + fi fi fi - printf '%s\n' "$SERVICE_NAME started on $(date)" >"/data/logs/start.log" + + # Write successful startup status to log + { + echo "$(date): Container startup completed successfully" + echo "Active services: $successful_services" + [ -n "$failed_services" ] && echo "Failed services: $failed_services" + echo "Status code: $retstatus" + } >> "/data/logs/start.log" + + printf '\nšŸŽ‰ All services initialized successfully!\n' + printf 'šŸ” Service monitoring will now begin...\n\n' return $retstatus } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1200,6 +1385,6 @@ export ENTRYPOINT_DATA_INIT_FILE DATA_DIR_INITIALIZED ENTRYPOINT_CONFIG_INIT_FIL export ENTRYPOINT_PID_FILE ENTRYPOINT_INIT_FILE ENTRYPOINT_FIRST_RUN # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # export the functions -export -f __get_pid __start_init_scripts __is_running __certbot __update_ssl_certs __create_ssl_cert +export -f __get_pid __start_init_scripts __is_running __certbot __update_ssl_certs __create_ssl_cert __no_exit __auto_detect_services # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # end of functions