#!/bin/bash set -euo pipefail # ============================================================ # Timezone # ============================================================ export TZ="${TZ:-Asia/Muscat}" ln -snf "/usr/share/zoneinfo/$TZ" /etc/localtime 2>/dev/null || true echo "$TZ" > /etc/timezone 2>/dev/null || true # ============================================================ # App config # ============================================================ APP_DIR="/home/user/app" HTDOCS_DIR="${APP_DIR}/htdocs" ARCHIVE_FILE="${APP_DIR}/htdocs.7z" ARCHIVE_PASSWORD="${ARCHIVE_PASSWORD:-}" ADMINER_SOURCE="${APP_DIR}/adminer.php" SQL_IMPORT_FILE="${APP_DIR}/wallacepos.sql" NODE_SERVER="${HTDOCS_DIR}/api/server.js" if [ ! -f "$NODE_SERVER" ] && [ -f "${HTDOCS_DIR}/server.js" ]; then NODE_SERVER="${HTDOCS_DIR}/server.js" fi # ============================================================ # DB config # ============================================================ DB_NAME="${DB_NAME:-wallacepos}" DB_USER="${DB_USER:-appuser}" DB_PASSWORD="${DB_PASSWORD:-apppass}" DB_ROOT_PASSWORD="${DB_ROOT_PASSWORD:-rootpass}" # ============================================================ # Paths # ============================================================ BUCKET_DIR="/home/user/mysql" BACKUP_DIR="${BUCKET_DIR}/backup" BASE_SNAPSHOT="${BACKUP_DIR}/base.sql.gz" LIVE_DB_DIR="/tmp/mysql-live" RUNTIME_DIR="/tmp/mysql-runtime" SOCKET="${RUNTIME_DIR}/mysql.sock" PIDFILE="${RUNTIME_DIR}/mysqld.pid" TC_LOG="${RUNTIME_DIR}/tc.log" SNAPSHOT_INTERVAL_SECS="${SNAPSHOT_INTERVAL_SECS:-30}" TAKE_BOOT_SNAPSHOT_IF_MISSING="${TAKE_BOOT_SNAPSHOT_IF_MISSING:-1}" BACKUP_LOCKFILE="${RUNTIME_DIR}/backup.lock" # ============================================================ # One merged persistent log only # ============================================================ LOG_DIR="${BUCKET_DIR}/logs" ALL_LOG="${LOG_DIR}/all.txt" LOGFILE="${ALL_LOG}" # ============================================================ # Monitor config # ============================================================ MONITOR_INTERVAL_SECS="${MONITOR_INTERVAL_SECS:-15}" HEARTBEAT_INTERVAL_SECS="${HEARTBEAT_INTERVAL_SECS:-30}" LOCAL_HEALTH_URLS="${LOCAL_HEALTH_URLS:-http://127.0.0.1:7860/ http://127.0.0.1:7860/index.php}" BOOT_ID="${BOOT_ID:-$(date +%Y%m%d-%H%M%S)-$$}" MYSQL_MONITOR_USER="${MYSQL_MONITOR_USER:-}" MYSQL_MONITOR_PASSWORD="${MYSQL_MONITOR_PASSWORD:-}" MYSQL_MONITOR_DB="${MYSQL_MONITOR_DB:-}" # ============================================================ # State # ============================================================ NODEPID="" DBPID="" BACKUPPID="" MONITORPID="" HEARTBEATPID="" APACHE_WRAPPER_PID="" LAST_SIGNAL="" CLEANUP_DONE="0" # ============================================================ # Helpers # ============================================================ timestamp() { date -Is } log() { local msg="[$(timestamp)] $*" printf '%s\n' "$msg" >> "$ALL_LOG" printf '%s\n' "$msg" } has_cmd() { command -v "$1" >/dev/null 2>&1 } safe_cat() { local file="$1" if [ -f "$file" ]; then cat "$file" 2>/dev/null || true else echo "n/a" fi } bytes_to_mb() { local value="$1" if [[ "$value" =~ ^[0-9]+$ ]]; then awk -v b="$value" 'BEGIN { printf "%.2f MB", b/1024/1024 }' else echo "$value" fi } kill_and_wait() { local pid="${1:-}" if [ -n "$pid" ] && kill -0 "$pid" >/dev/null 2>&1; then kill "$pid" >/dev/null 2>&1 || true wait "$pid" >/dev/null 2>&1 || true fi } apache_is_running() { pgrep -x apache2 >/dev/null 2>&1 || pgrep -x httpd >/dev/null 2>&1 } get_apache_master_pid() { pgrep -xo apache2 2>/dev/null || pgrep -xo httpd 2>/dev/null || true } node_is_running() { [ -n "${NODEPID:-}" ] && kill -0 "${NODEPID}" >/dev/null 2>&1 } mysql_noauth() { mysql --protocol=socket --socket="$SOCKET" -uroot "$@" } mysql_auth() { mysql --protocol=socket --socket="$SOCKET" -uroot -p"${DB_ROOT_PASSWORD}" "$@" } mysqladmin_noauth() { mysqladmin --protocol=socket --socket="$SOCKET" "$@" } mysqladmin_auth() { mysqladmin --protocol=socket --socket="$SOCKET" -uroot -p"${DB_ROOT_PASSWORD}" "$@" } mysql_is_running() { mysqladmin_auth ping >/dev/null 2>&1 } prepare_dirs() { mkdir -p "$BUCKET_DIR" "$BACKUP_DIR" "$LOG_DIR" "$LIVE_DB_DIR" "$RUNTIME_DIR" touch "$ALL_LOG" chmod 777 "$LOG_DIR" 2>/dev/null || true chmod 666 "$ALL_LOG" 2>/dev/null || true chown -R mysql:mysql "$LIVE_DB_DIR" "$RUNTIME_DIR" 2>/dev/null || true chmod 700 "$LIVE_DB_DIR" 2>/dev/null || true } debug_paths() { log "[DEBUG] BOOT_ID=$BOOT_ID" log "[DEBUG] APP_DIR=$APP_DIR" log "[DEBUG] HTDOCS_DIR=$HTDOCS_DIR" log "[DEBUG] BUCKET_DIR=$BUCKET_DIR" log "[DEBUG] BACKUP_DIR=$BACKUP_DIR" log "[DEBUG] BASE_SNAPSHOT=$BASE_SNAPSHOT" log "[DEBUG] LIVE_DB_DIR=$LIVE_DB_DIR" log "[DEBUG] RUNTIME_DIR=$RUNTIME_DIR" log "[DEBUG] SOCKET=$SOCKET" log "[DEBUG] LOGFILE=$LOGFILE" log "[DEBUG] ALL_LOG=$ALL_LOG" log "[DEBUG] SNAPSHOT_INTERVAL_SECS=$SNAPSHOT_INTERVAL_SECS" log "[DEBUG] MONITOR_INTERVAL_SECS=$MONITOR_INTERVAL_SECS" log "[DEBUG] HEARTBEAT_INTERVAL_SECS=$HEARTBEAT_INTERVAL_SECS" } # ============================================================ # Command detection # ============================================================ INIT_CMD="" command -v mariadb-install-db >/dev/null 2>&1 && INIT_CMD="mariadb-install-db" command -v mysql_install_db >/dev/null 2>&1 && INIT_CMD="${INIT_CMD:-mysql_install_db}" if [ -z "$INIT_CMD" ]; then echo "[DB] ❌ No init tool found (mariadb-install-db / mysql_install_db)." >&2 exit 1 fi MYSQL_DUMP_CMD="" command -v mariadb-dump >/dev/null 2>&1 && MYSQL_DUMP_CMD="mariadb-dump" command -v mysqldump >/dev/null 2>&1 && MYSQL_DUMP_CMD="${MYSQL_DUMP_CMD:-mysqldump}" if [ -z "$MYSQL_DUMP_CMD" ]; then echo "[DB] ❌ No dump tool found (mariadb-dump / mysqldump)." >&2 exit 1 fi # ============================================================ # Configure PHP error logging directly to all.txt # ============================================================ configure_php_logging() { log "[PHP] Configuring PHP error logging to ${ALL_LOG}" local ini_dir for ini_dir in /etc/php/*/apache2/conf.d /etc/php/*/cli/conf.d; do [ -d "$ini_dir" ] || continue cat > "${ini_dir}/99-space-logging.ini" </dev/null || true } # ============================================================ # Configure Apache logging directly to all.txt # ============================================================ configure_apache_logging() { log "[WEB] Configuring Apache logging to ${ALL_LOG}" mkdir -p /var/log/apache2 /var/run/apache2 /var/lock/apache2 rm -f /var/log/apache2/access.log \ /var/log/apache2/error.log \ /var/log/apache2/other_vhosts_access.log ln -sf "$ALL_LOG" /var/log/apache2/access.log ln -sf "$ALL_LOG" /var/log/apache2/error.log ln -sf "$ALL_LOG" /var/log/apache2/other_vhosts_access.log chmod 666 "$ALL_LOG" 2>/dev/null || true } # ============================================================ # App helpers # ============================================================ ensure_app_files() { log "[APP] Checking application files..." if [ -d "$HTDOCS_DIR" ] && [ -n "$(find "$HTDOCS_DIR" -mindepth 1 -maxdepth 1 2>/dev/null)" ]; then log "[APP] htdocs already exists and is not empty, skipping extraction." else if [ ! -f "$ARCHIVE_FILE" ]; then log "[APP] ❌ Missing archive: $ARCHIVE_FILE" exit 1 fi if [ -z "$ARCHIVE_PASSWORD" ]; then log "[APP] ❌ ARCHIVE_PASSWORD secret is not set" exit 1 fi mkdir -p "$HTDOCS_DIR" log "[APP] Extracting encrypted archive..." 7z x "$ARCHIVE_FILE" -o"$HTDOCS_DIR" "-p$ARCHIVE_PASSWORD" -y >/dev/null log "[APP] ✅ Extraction complete" rm -f "$ARCHIVE_FILE" fi if [ -f "$ADMINER_SOURCE" ]; then cp -f "$ADMINER_SOURCE" "${HTDOCS_DIR}/adminer.php" fi if [ -f "${HTDOCS_DIR}/api/package.json" ]; then if [ ! -d "${HTDOCS_DIR}/api/node_modules" ]; then log "[NODE] Installing dependencies in ${HTDOCS_DIR}/api ..." cd "${HTDOCS_DIR}/api" npm install --omit=dev else log "[NODE] node_modules already exists in ${HTDOCS_DIR}/api, skipping npm install." fi elif [ -f "${HTDOCS_DIR}/package.json" ]; then if [ ! -d "${HTDOCS_DIR}/node_modules" ]; then log "[NODE] Installing dependencies in ${HTDOCS_DIR} ..." cd "${HTDOCS_DIR}" npm install --omit=dev else log "[NODE] node_modules already exists in ${HTDOCS_DIR}, skipping npm install." fi else log "[NODE] No package.json found, skipping npm install." fi } fix_app_permissions() { chown -R www-data:www-data "$HTDOCS_DIR" 2>/dev/null || true find "$HTDOCS_DIR" -type d -exec chmod 755 {} \; 2>/dev/null || true find "$HTDOCS_DIR" -type f -exec chmod 644 {} \; 2>/dev/null || true } # ============================================================ # MySQL lifecycle helpers # ============================================================ wait_for_mysql_noauth() { for i in {1..60}; do if mysqladmin_noauth ping >/dev/null 2>&1; then return 0 fi sleep 1 done return 1 } wait_for_mysql_auth() { for i in {1..60}; do if mysqladmin_auth ping >/dev/null 2>&1; then return 0 fi sleep 1 done return 1 } live_datadir_initialized() { [ -d "${LIVE_DB_DIR}/mysql" ] && [ -n "$(find "${LIVE_DB_DIR}/mysql" -mindepth 1 -maxdepth 1 2>/dev/null)" ] } init_live_datadir_if_needed() { if live_datadir_initialized; then log "[DB] Existing live datadir found at ${LIVE_DB_DIR}, reusing it." return fi log "[DB] Initializing fresh live datadir at ${LIVE_DB_DIR} ..." rm -rf "${LIVE_DB_DIR:?}/"* "${RUNTIME_DIR:?}/"* 2>/dev/null || true if ! $INIT_CMD --user=mysql --datadir="$LIVE_DB_DIR" --auth-root-authentication-method=normal --skip-test-db >/dev/null 2>&1; then if ! $INIT_CMD --user=mysql --datadir="$LIVE_DB_DIR" --skip-test-db >/dev/null 2>&1; then $INIT_CMD --user=mysql --datadir="$LIVE_DB_DIR" >/dev/null fi fi chown -R mysql:mysql "$LIVE_DB_DIR" "$RUNTIME_DIR" 2>/dev/null || true log "[DB] ✅ Live datadir initialized" } start_bootstrap_mysql() { log "[DB] Starting bootstrap MariaDB ..." rm -f "$SOCKET" "$PIDFILE" "$TC_LOG" mysqld \ --user=mysql \ --datadir="$LIVE_DB_DIR" \ --socket="$SOCKET" \ --pid-file="$PIDFILE" \ --log-error="$LOGFILE" \ --log-tc="$TC_LOG" \ --skip-networking \ --skip-log-bin & DBPID=$! if ! wait_for_mysql_noauth; then log "[DB] ❌ Bootstrap MariaDB failed." tail -n 120 "$LOGFILE" || true exit 1 fi log "[DB] ✅ Bootstrap MariaDB ready" } bootstrap_core_sql() { log "[DB] Applying core DB/user setup ..." mysql_noauth </dev/null 2>&1 || mysqladmin_noauth shutdown >/dev/null 2>&1 || true fi if [ -n "${DBPID}" ] && kill -0 "${DBPID}" >/dev/null 2>&1; then wait "${DBPID}" >/dev/null 2>&1 || true fi DBPID="" } start_main_mysql() { log "[DB] Starting main MariaDB ..." rm -f "$SOCKET" "$PIDFILE" "$TC_LOG" mysqld \ --user=mysql \ --datadir="$LIVE_DB_DIR" \ --socket="$SOCKET" \ --pid-file="$PIDFILE" \ --log-error="$LOGFILE" \ --log-tc="$TC_LOG" \ --bind-address=127.0.0.1 \ --port=3306 & DBPID=$! if ! wait_for_mysql_auth; then log "[DB] ❌ Main MariaDB failed to start." tail -n 120 "$LOGFILE" || true exit 1 fi chmod 666 "$ALL_LOG" 2>/dev/null || true log "[DB] ✅ Main MariaDB ready (socket: $SOCKET, tcp: 127.0.0.1:3306)" } # ============================================================ # Restore / seed # ============================================================ restore_base_snapshot_if_present() { if [ ! -f "$BASE_SNAPSHOT" ]; then log "[RESTORE] No base snapshot found, skipping restore." return 1 fi log "[RESTORE] Restoring base snapshot from ${BASE_SNAPSHOT} ..." gzip -dc "$BASE_SNAPSHOT" | mysql_auth "$DB_NAME" log "[RESTORE] ✅ Base snapshot restored" return 0 } seed_if_no_snapshot() { if [ -f "$BASE_SNAPSHOT" ]; then return 0 fi if [ -f "$SQL_IMPORT_FILE" ]; then log "[SEED] No base snapshot found, importing seed SQL from ${SQL_IMPORT_FILE} ..." mysql_auth "$DB_NAME" < "$SQL_IMPORT_FILE" log "[SEED] ✅ Seed import complete" else log "[SEED] No base snapshot and no seed SQL found. Starting with empty database." fi } # ============================================================ # Backup helpers # ============================================================ create_base_snapshot() { if [ -f "$BACKUP_LOCKFILE" ]; then log "[BACKUP] Previous snapshot still running, skipping this cycle." return 0 fi touch "$BACKUP_LOCKFILE" trap 'rm -f "$BACKUP_LOCKFILE"' RETURN local tmp_snapshot tmp_snapshot="${BASE_SNAPSHOT}.tmp" log "[BACKUP] Creating base snapshot ..." "$MYSQL_DUMP_CMD" \ --protocol=socket \ --socket="$SOCKET" \ -uroot \ -p"${DB_ROOT_PASSWORD}" \ --single-transaction \ --quick \ --routines \ --events \ --triggers \ --hex-blob \ "$DB_NAME" \ | gzip -1 > "$tmp_snapshot" mv -f "$tmp_snapshot" "$BASE_SNAPSHOT" log "[BACKUP] ✅ Base snapshot updated: ${BASE_SNAPSHOT}" } start_snapshot_loop() { log "[BACKUP] Starting single-file snapshot loop every ${SNAPSHOT_INTERVAL_SECS}s ..." ( while true; do sleep "$SNAPSHOT_INTERVAL_SECS" if ! mysql_is_running; then log "[BACKUP] MySQL ping failed; skipping snapshot cycle." continue fi create_base_snapshot || true done ) & BACKUPPID=$! log "[BACKUP] ✅ Snapshot loop running (pid: ${BACKUPPID})" } # ============================================================ # Services # ============================================================ start_node() { if [ -f "$NODE_SERVER" ]; then log "[NODE] Starting: $NODE_SERVER" node "$NODE_SERVER" >> "$ALL_LOG" 2>&1 & NODEPID=$! log "[NODE] ✅ Running (pid: $NODEPID)" else log "[NODE] (skip) server.js not found at: $NODE_SERVER" fi } start_apache() { log "[WEB] Starting Apache for ${HTDOCS_DIR} ..." apachectl -D FOREGROUND >> "$ALL_LOG" 2>&1 & APACHE_WRAPPER_PID=$! sleep 2 if apache_is_running; then log "[WEB] ✅ Apache running (master pid: $(get_apache_master_pid), wrapper pid: ${APACHE_WRAPPER_PID})" else log "[WEB] ❌ Apache failed to start." return 1 fi } # ============================================================ # Monitor loop # ============================================================ monitor_snapshot() { log "[MONITOR] SNAPSHOT_BEGIN boot_id=${BOOT_ID}" if [ -r /proc/uptime ]; then log "[MONITOR] uptime_seconds=$(awk '{print int($1)}' /proc/uptime)" fi if [ -r /proc/loadavg ]; then log "[MONITOR] loadavg=$(cat /proc/loadavg)" fi if has_cmd free; then while IFS= read -r line; do log "[MONITOR][FREE] $line" done < <(free -m) fi if [ -f /sys/fs/cgroup/cgroup.controllers ]; then local memory_current memory_max memory_peak pids_current pids_max memory_events cpu_stat memory_current="$(safe_cat /sys/fs/cgroup/memory.current)" memory_max="$(safe_cat /sys/fs/cgroup/memory.max)" memory_peak="$(safe_cat /sys/fs/cgroup/memory.peak)" pids_current="$(safe_cat /sys/fs/cgroup/pids.current)" pids_max="$(safe_cat /sys/fs/cgroup/pids.max)" cpu_stat="$(tr '\n' ' ' < /sys/fs/cgroup/cpu.stat 2>/dev/null || echo n/a)" memory_events="$(tr '\n' ' ' < /sys/fs/cgroup/memory.events 2>/dev/null || echo n/a)" log "[MONITOR][CGROUP] version=v2 memory.current=$memory_current ($(bytes_to_mb "$memory_current")) memory.max=$memory_max memory.peak=$memory_peak ($(bytes_to_mb "$memory_peak")) pids.current=$pids_current pids.max=$pids_max" log "[MONITOR][CGROUP] cpu.stat=$cpu_stat" log "[MONITOR][CGROUP] memory.events=$memory_events" fi while IFS= read -r line; do log "[MONITOR][DF] $line" done < <(df -hP / "$APP_DIR" "$LIVE_DB_DIR" "$BUCKET_DIR" "$RUNTIME_DIR" 2>/dev/null | awk '!seen[$NF]++') while IFS= read -r line; do log "[MONITOR][PS] $line" done < <(ps -eo pid,ppid,comm,%cpu,%mem,rss,vsz,etime,stat,args --sort=-rss 2>/dev/null | head -n 12) if has_cmd ss; then while IFS= read -r line; do log "[MONITOR][TCP] $line" done < <(ss -tanH 2>/dev/null | awk '{ s[$1]++ } END { for (k in s) printf "%s=%d ", k, s[k]; print "" }') fi local url for url in $LOCAL_HEALTH_URLS; do if has_cmd curl; then local out out="$(curl -k -sS -o /dev/null --max-time 5 -w "code=%{http_code} connect=%{time_connect}s starttransfer=%{time_starttransfer}s total=%{time_total}s size=%{size_download}" "$url" 2>&1 || true)" log "[MONITOR][HTTP] $url => $out" fi done log "[MONITOR][SERVICE] apache_running=$([ apache_is_running && echo yes || echo no ]) node_running=$([ node_is_running && echo yes || echo no ]) mysql_running=$([ mysql_is_running && echo yes || echo no ])" if has_cmd mysql && [ -n "$MYSQL_MONITOR_USER" ] && mysql_is_running; then while IFS= read -r line; do log "[MONITOR][MYSQL-STATUS] $line" done < <( MYSQL_PWD="$MYSQL_MONITOR_PASSWORD" \ mysql --protocol=socket --socket="$SOCKET" \ -u "$MYSQL_MONITOR_USER" \ ${MYSQL_MONITOR_DB:+ "$MYSQL_MONITOR_DB"} \ -NBe " SHOW GLOBAL STATUS WHERE Variable_name IN ( 'Threads_connected', 'Threads_running', 'Max_used_connections', 'Aborted_connects', 'Aborted_clients', 'Slow_queries', 'Questions', 'Uptime' ); " 2>/dev/null || true ) fi log "[MONITOR] SNAPSHOT_END boot_id=${BOOT_ID}" } start_monitor_loop() { log "[MONITOR] Starting monitor loop every ${MONITOR_INTERVAL_SECS}s ..." ( while true; do monitor_snapshot || true sleep "$MONITOR_INTERVAL_SECS" done ) & MONITORPID=$! log "[MONITOR] ✅ Running (pid: ${MONITORPID})" } start_heartbeat_loop() { log "[HEARTBEAT] Starting heartbeat loop every ${HEARTBEAT_INTERVAL_SECS}s ..." ( while true; do sleep "$HEARTBEAT_INTERVAL_SECS" log "[HEARTBEAT] launcher_alive=yes boot_id=${BOOT_ID} apache_running=$([ apache_is_running && echo yes || echo no ]) node_running=$([ node_is_running && echo yes || echo no ]) mysql_running=$([ mysql_is_running && echo yes || echo no ])" done ) & HEARTBEATPID=$! log "[HEARTBEAT] ✅ Running (pid: ${HEARTBEATPID})" } # ============================================================ # Keep launcher alive forever # ============================================================ wait_forever() { log "[READY] Core services started; launcher will stay alive until container stops. boot_id=${BOOT_ID}" while true; do sleep 3600 done } # ============================================================ # Cleanup / lifecycle # ============================================================ cleanup() { local exit_code="${1:-0}" if [ "${CLEANUP_DONE}" = "1" ]; then return fi CLEANUP_DONE="1" log "[SHUTDOWN] Cleaning up ... exit_code=${exit_code} signal=${LAST_SIGNAL:-none} boot_id=${BOOT_ID}" kill_and_wait "$BACKUPPID" kill_and_wait "$MONITORPID" kill_and_wait "$HEARTBEATPID" if [ -S "$SOCKET" ]; then create_base_snapshot || true fi if [ -n "${LAST_SIGNAL:-}" ] && [ "${LAST_SIGNAL}" != "none" ]; then log "[SHUTDOWN] Real stop signal received, stopping services..." kill_and_wait "$NODEPID" if apache_is_running; then pkill -TERM -x apache2 >/dev/null 2>&1 || pkill -TERM -x httpd >/dev/null 2>&1 || true sleep 2 pkill -KILL -x apache2 >/dev/null 2>&1 || pkill -KILL -x httpd >/dev/null 2>&1 || true fi stop_mysql_if_running || true else log "[SHUTDOWN] No real stop signal; leaving services alone." fi log "[SHUTDOWN] Done." } handle_signal() { LAST_SIGNAL="$1" log "[SIGNAL] Received ${1} boot_id=${BOOT_ID}" exit 0 } on_exit() { local exit_code="$1" cleanup "$exit_code" } trap 'handle_signal TERM' TERM trap 'handle_signal INT' INT trap 'on_exit $?' EXIT # ============================================================ # Boot sequence # ============================================================ prepare_dirs log "===== Application Startup at $(timestamp) =====" log "[TIME] zone=$(date +%Z) offset=$(date +%z) TZ=${TZ}" log "[BOOT] boot_id=${BOOT_ID} host=$(hostname)" log "[STAGE] prepare_dirs done" ensure_app_files log "[STAGE] ensure_app_files done" fix_app_permissions log "[STAGE] fix_app_permissions done" configure_php_logging log "[STAGE] configure_php_logging done" configure_apache_logging log "[STAGE] configure_apache_logging done" debug_paths log "[STAGE] debug_paths done" rm -rf "${LIVE_DB_DIR:?}/"* "${RUNTIME_DIR:?}/"* 2>/dev/null || true log "[STAGE] runtime reset done" init_live_datadir_if_needed log "[STAGE] init_live_datadir_if_needed done" start_bootstrap_mysql log "[STAGE] start_bootstrap_mysql done" bootstrap_core_sql log "[STAGE] bootstrap_core_sql done" if restore_base_snapshot_if_present; then log "[RESTORE] Restored from single backup file." else seed_if_no_snapshot fi log "[STAGE] restore_or_seed done" stop_mysql_if_running log "[STAGE] stop_bootstrap_mysql done" start_main_mysql log "[STAGE] start_main_mysql done" if [ "${TAKE_BOOT_SNAPSHOT_IF_MISSING}" = "1" ] && [ ! -f "$BASE_SNAPSHOT" ]; then create_base_snapshot fi log "[STAGE] initial_snapshot_check done" start_snapshot_loop log "[STAGE] start_snapshot_loop done" start_monitor_loop log "[STAGE] start_monitor_loop done" start_node log "[STAGE] start_node done" start_apache log "[STAGE] start_apache done" start_heartbeat_loop log "[STAGE] start_heartbeat_loop done" wait_forever