#!/usr/bin/env bash set -Eeuo pipefail IFS=$'\n\t' umask 022 STEP_NAME="STEP038_2156_PRESENZE_DISTRIBUITE_GENERATOR_DEEP_VALIDATION_PATCH_VERIFIED_20260730" EXPECTED_HOST="ax-accessi" EXPECTED_IP="192.168.215.6" WORKER_PATH="/usr/local/sbin/dvm-presenze-portal-generator" SOURCE_ROOT="/var/www/html/presenze.distribuite" SOURCE_DB="presenze_distribuite" SOURCE_DOMAIN="presenze.distribuite.axuno.it" SOURCE_VHOST="/etc/apache2/sites-available/${SOURCE_DOMAIN}.conf" OUTPUT_DIR="$PWD" RUN_ID="$(date +%Y%m%d_%H%M%S)" PLAN_SLUG="deep-check-$(date +%H%M%S)" WORKDIR="$(mktemp -d "/tmp/${STEP_NAME}.XXXXXX")" REPORT_DIR="${WORKDIR}/report" ZIP_PATH="${OUTPUT_DIR}/${STEP_NAME}_${RUN_ID}.zip" CANDIDATE="${WORKDIR}/dvm-presenze-portal-generator" BACKUP_PATH="${WORKDIR}/worker.previous" HAD_PREVIOUS=0 INSTALLED_NEW=0 FINISHED=0 mkdir -p "$REPORT_DIR" log() { printf '%s\n' "$*" | tee -a "${REPORT_DIR}/run.log" } cleanup() { local rc=$? if [[ $rc -ne 0 && $FINISHED -eq 0 ]]; then if [[ $HAD_PREVIOUS -eq 1 && -f "$BACKUP_PATH" ]]; then install -o root -g root -m 0750 "$BACKUP_PATH" "$WORKER_PATH" || true elif [[ $INSTALLED_NEW -eq 1 ]]; then rm -f -- "$WORKER_PATH" || true fi fi rm -rf -- "$WORKDIR" exit "$rc" } trap cleanup EXIT trap 'log "ERRORE linea ${LINENO}: ${BASH_COMMAND}"' ERR if [[ ${EUID} -ne 0 ]]; then log "ERRORE: eseguire come root tramite sudo." exit 1 fi HOST_NOW="$(hostname -s 2>/dev/null || hostname)" IPS_NOW="$(hostname -I 2>/dev/null || true)" if [[ "$HOST_NOW" != "$EXPECTED_HOST" ]] || [[ " $IPS_NOW " != *" $EXPECTED_IP "* ]]; then log "ERRORE: server errato. Rilevato host=${HOST_NOW}, IP=${IPS_NOW:-N/D}; atteso ${EXPECTED_HOST} / ${EXPECTED_IP}." exit 1 fi for cmd in bash install rsync mysql mysqldump apache2ctl a2ensite a2dissite flock zip sha256sum readlink sed grep find mktemp curl cp ln rm head awk stat; do command -v "$cmd" >/dev/null 2>&1 || { log "ERRORE: comando mancante: $cmd"; exit 1; } done [[ -d "$SOURCE_ROOT" ]] || { log "ERRORE: base assente: $SOURCE_ROOT"; exit 1; } [[ -f "$SOURCE_ROOT/phpgen_settings.php" ]] || { log "ERRORE: configurazione base assente: $SOURCE_ROOT/phpgen_settings.php"; exit 1; } [[ -f "$SOURCE_VHOST" ]] || { log "ERRORE: vhost base assente: $SOURCE_VHOST"; exit 1; } [[ -x "$WORKER_PATH" ]] || { log "ERRORE: worker STEP032 assente o non eseguibile: $WORKER_PATH"; exit 1; } cp -a -- "$WORKER_PATH" "$BACKUP_PATH" HAD_PREVIOUS=1 cat > "$CANDIDATE" <<'WORKER' #!/usr/bin/env bash set -Eeuo pipefail IFS=$'\n\t' umask 022 SOURCE_ROOT="${DVM_GEN_SOURCE_ROOT:-/var/www/html/presenze.distribuite}" SOURCE_DB="${DVM_GEN_SOURCE_DB:-presenze_distribuite}" SOURCE_DOMAIN="${DVM_GEN_SOURCE_DOMAIN:-presenze.distribuite.axuno.it}" WEB_ROOT="${DVM_GEN_WEB_ROOT:-/var/www/html}" SITES_AVAILABLE="${DVM_GEN_SITES_AVAILABLE:-/etc/apache2/sites-available}" SITES_ENABLED="${DVM_GEN_SITES_ENABLED:-/etc/apache2/sites-enabled}" SOURCE_VHOST="${DVM_GEN_SOURCE_VHOST:-${SITES_AVAILABLE}/${SOURCE_DOMAIN}.conf}" LOCK_FILE="${DVM_GEN_LOCK_FILE:-/var/lock/dvm-presenze-portal-generator.lock}" LOG_FILE="${DVM_GEN_LOG_FILE:-/var/log/dvm-presenze-portal-generator.log}" MYSQL_BIN="${DVM_GEN_MYSQL_BIN:-mysql}" MYSQLDUMP_BIN="${DVM_GEN_MYSQLDUMP_BIN:-mysqldump}" APACHECTL_BIN="${DVM_GEN_APACHECTL_BIN:-apache2ctl}" A2ENSITE_BIN="${DVM_GEN_A2ENSITE_BIN:-a2ensite}" A2DISSITE_BIN="${DVM_GEN_A2DISSITE_BIN:-a2dissite}" RSYNC_BIN="${DVM_GEN_RSYNC_BIN:-rsync}" CURL_BIN="${DVM_GEN_CURL_BIN:-curl}" EXPECTED_HOST="${DVM_GEN_EXPECTED_HOST:-ax-accessi}" EXPECTED_IP="${DVM_GEN_EXPECTED_IP:-192.168.215.6}" TEST_MODE="${DVM_GEN_TEST_MODE:-0}" COMMAND="${1:-}" SLUG="${2:-}" TARGET_DOMAIN="" TARGET_ROOT="" TARGET_DB="" TARGET_VHOST="" TARGET_VHOST_NAME="" STAGE_ROOT="" HTTP_BODY="" HTTP_HEADERS="" CREATED_DB=0 CREATED_ROOT=0 CREATED_VHOST=0 ENABLED_SITE=0 IN_ROLLBACK=0 SOURCE_OBJECTS=0 TARGET_OBJECTS=0 HTTP_STATUS="" usage() { cat <<'USAGE' Uso: dvm-presenze-portal-generator inspect dvm-presenze-portal-generator plan dvm-presenze-portal-generator create Regole slug: lettere minuscole, numeri e trattini; deve iniziare e finire con lettera o numero. Nomi generati: dominio: presenze..axuno.it cartella: /var/www/html/presenze. database: presenze_ USAGE } emit() { printf '%s=%s\n' "$1" "$2" } log_event() { local message="$1" printf '%s %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$message" >> "$LOG_FILE" if [[ "$TEST_MODE" != "1" ]]; then chown root:root "$LOG_FILE" 2>/dev/null || true chmod 0640 "$LOG_FILE" 2>/dev/null || true fi } validate_server() { [[ "$TEST_MODE" == "1" ]] && return 0 local host_now ips_now host_now="$(hostname -s 2>/dev/null || hostname)" ips_now="$(hostname -I 2>/dev/null || true)" [[ "$host_now" == "$EXPECTED_HOST" ]] || { echo "ERRORE: host errato: $host_now" >&2; exit 20; } [[ " $ips_now " == *" $EXPECTED_IP "* ]] || { echo "ERRORE: IP atteso non presente: $ips_now" >&2; exit 21; } } validate_slug() { [[ "$SLUG" =~ ^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$ ]] || { echo "ERRORE: slug non valido: $SLUG" >&2 exit 22 } case "$SLUG" in distribuite|distro|silvelox|dvmservice|webservice) echo "ERRORE: slug riservato: $SLUG" >&2 exit 23 ;; esac } build_names() { local db_suffix db_suffix="${SLUG//-/_}" TARGET_DOMAIN="presenze.${SLUG}.axuno.it" TARGET_ROOT="${WEB_ROOT}/presenze.${SLUG}" TARGET_DB="presenze_${db_suffix}" TARGET_VHOST_NAME="${TARGET_DOMAIN}.conf" TARGET_VHOST="${SITES_AVAILABLE}/${TARGET_VHOST_NAME}" } mysql_scalar() { "$MYSQL_BIN" --batch --skip-column-names -e "$1" } database_exists() { local db="$1" result result="$(mysql_scalar "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='${db}'" 2>/dev/null || true)" [[ "$result" == "$db" ]] } database_object_count() { local db="$1" mysql_scalar "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='${db}'" } extract_php_setting() { local key="$1" file="$2" value value="$(sed -nE "s/^[[:space:]]*'${key}'[[:space:]]*=>[[:space:]]*'([^']*)'.*/\\1/p" "$file" | head -n 1)" [[ -n "$value" ]] || { echo "ERRORE: parametro ${key} non trovato in ${file}" >&2 return 1 } printf '%s' "$value" } application_db_access() { local settings_file="$1" expected_db="$2" local app_host app_user app_password app_database result app_host="$(extract_php_setting host "$settings_file")" app_user="$(extract_php_setting username "$settings_file")" app_password="$(extract_php_setting password "$settings_file")" app_database="$(extract_php_setting database "$settings_file")" [[ "$app_database" == "$expected_db" ]] || { echo "ERRORE: database applicativo inatteso in ${settings_file}: ${app_database}" >&2 return 1 } result="$(MYSQL_PWD="$app_password" "$MYSQL_BIN" --batch --skip-column-names --host="$app_host" --user="$app_user" "$app_database" -e 'SELECT 1' 2>/dev/null || true)" [[ "$result" == "1" ]] || { echo "ERRORE: l'utente applicativo non accede al database ${expected_db}" >&2 return 1 } } check_source() { [[ -d "$SOURCE_ROOT" ]] || { echo "ERRORE: base assente: $SOURCE_ROOT" >&2; exit 30; } [[ -f "$SOURCE_ROOT/phpgen_settings.php" ]] || { echo "ERRORE: phpgen_settings.php assente" >&2; exit 31; } [[ -f "$SOURCE_VHOST" ]] || { echo "ERRORE: vhost sorgente assente: $SOURCE_VHOST" >&2; exit 32; } [[ -L "$SOURCE_ROOT/bower_components" ]] || { echo "ERRORE: symlink bower_components assente" >&2; exit 33; } [[ -L "$SOURCE_ROOT/node_modules" ]] || { echo "ERRORE: symlink node_modules assente" >&2; exit 34; } [[ -d "$(readlink -f "$SOURCE_ROOT/bower_components")" ]] || { echo "ERRORE: target bower_components interrotto" >&2; exit 35; } [[ -d "$(readlink -f "$SOURCE_ROOT/node_modules")" ]] || { echo "ERRORE: target node_modules interrotto" >&2; exit 36; } database_exists "$SOURCE_DB" || { echo "ERRORE: database sorgente assente: $SOURCE_DB" >&2; exit 37; } grep -Fq "'database' => '${SOURCE_DB}'" "$SOURCE_ROOT/phpgen_settings.php" || { echo "ERRORE: riferimento database sorgente non trovato in phpgen_settings.php" >&2 exit 38 } application_db_access "$SOURCE_ROOT/phpgen_settings.php" "$SOURCE_DB" || exit 45 grep -Fq "$SOURCE_DOMAIN" "$SOURCE_VHOST" || { echo "ERRORE: dominio sorgente non trovato nel vhost" >&2; exit 39; } grep -Fq "$SOURCE_ROOT" "$SOURCE_VHOST" || { echo "ERRORE: DocumentRoot sorgente non trovato nel vhost" >&2; exit 40; } SOURCE_OBJECTS="$(database_object_count "$SOURCE_DB")" [[ "$SOURCE_OBJECTS" =~ ^[0-9]+$ ]] && (( SOURCE_OBJECTS > 0 )) || { echo "ERRORE: inventario database sorgente non valido: ${SOURCE_OBJECTS}" >&2 exit 46 } } check_target_absent() { [[ ! -e "$TARGET_ROOT" ]] || { echo "ERRORE: cartella già esistente: $TARGET_ROOT" >&2; exit 41; } [[ ! -e "$TARGET_VHOST" ]] || { echo "ERRORE: vhost già esistente: $TARGET_VHOST" >&2; exit 42; } [[ ! -e "$SITES_ENABLED/$TARGET_VHOST_NAME" ]] || { echo "ERRORE: sito già abilitato: $TARGET_VHOST_NAME" >&2; exit 43; } ! database_exists "$TARGET_DB" || { echo "ERRORE: database già esistente: $TARGET_DB" >&2; exit 44; } } clear_runtime_dir() { local root="$1" local relative="$2" local dir="${root}/${relative}" [[ -d "$dir" ]] || return 0 find "$dir" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} + } patch_database_name() { local file="$1" count count="$(grep -F -c "'database' => '${SOURCE_DB}'" "$file" || true)" [[ "$count" == "1" ]] || { echo "ERRORE: atteso un solo riferimento a ${SOURCE_DB} in ${file}, trovati ${count}" >&2 return 1 } sed -i "s/'database' => '${SOURCE_DB}'/'database' => '${TARGET_DB}'/" "$file" grep -Fq "'database' => '${TARGET_DB}'" "$file" ! grep -Fq "'database' => '${SOURCE_DB}'" "$file" } render_vhost() { local destination="$1" cp -- "$SOURCE_VHOST" "$destination" sed -i \ -e "s#${SOURCE_DOMAIN}#${TARGET_DOMAIN}#g" \ -e "s#${SOURCE_ROOT}#${TARGET_ROOT}#g" \ "$destination" grep -Fq "ServerName ${TARGET_DOMAIN}" "$destination" grep -Fq "DocumentRoot ${TARGET_ROOT}" "$destination" grep -Fq "" "$destination" ! grep -Fq "$SOURCE_DOMAIN" "$destination" ! grep -Fq "$SOURCE_ROOT" "$destination" } local_http_check() { HTTP_BODY="$(mktemp)" HTTP_HEADERS="$(mktemp)" HTTP_STATUS="$("$CURL_BIN" --silent --show-error --max-time 30 \ --header "Host: ${TARGET_DOMAIN}" \ --dump-header "$HTTP_HEADERS" \ --output "$HTTP_BODY" \ --write-out '%{http_code}' \ 'http://127.0.0.1/' || true)" [[ "$HTTP_STATUS" =~ ^[123][0-9][0-9]$ ]] || { echo "ERRORE: test HTTP locale fallito per ${TARGET_DOMAIN}, stato=${HTTP_STATUS:-N/D}" >&2 return 1 } if grep -Eiq 'Fatal error|Uncaught (Error|Exception)|Access denied for user|Unknown database' "$HTTP_BODY"; then echo "ERRORE: risposta HTTP contiene un errore applicativo fatale per ${TARGET_DOMAIN}" >&2 return 1 fi rm -f -- "$HTTP_BODY" "$HTTP_HEADERS" HTTP_BODY="" HTTP_HEADERS="" } rollback() { local rc=$? rm -f -- "${HTTP_BODY:-}" "${HTTP_HEADERS:-}" 2>/dev/null || true [[ "$COMMAND" == "create" ]] || exit "$rc" [[ $rc -ne 0 ]] || exit 0 [[ $IN_ROLLBACK -eq 0 ]] || exit "$rc" IN_ROLLBACK=1 trap - ERR echo "ROLLBACK: ripristino dopo errore." >&2 if [[ $ENABLED_SITE -eq 1 ]]; then "$A2DISSITE_BIN" "$TARGET_VHOST_NAME" >/dev/null 2>&1 || true fi if [[ $CREATED_VHOST -eq 1 ]]; then rm -f -- "$TARGET_VHOST" "$SITES_ENABLED/$TARGET_VHOST_NAME" || true fi if [[ $CREATED_ROOT -eq 1 ]]; then rm -rf --one-file-system -- "$TARGET_ROOT" || true fi if [[ -n "$STAGE_ROOT" && -e "$STAGE_ROOT" ]]; then rm -rf --one-file-system -- "$STAGE_ROOT" || true fi if [[ $CREATED_DB -eq 1 ]]; then "$MYSQL_BIN" -e "DROP DATABASE IF EXISTS \`${TARGET_DB}\`" >/dev/null 2>&1 || true fi "$APACHECTL_BIN" configtest >/dev/null 2>&1 || true "$APACHECTL_BIN" graceful >/dev/null 2>&1 || true log_event "FAILED slug=${SLUG} rollback=completed exit=${rc}" exit "$rc" } trap rollback EXIT trap 'echo "ERRORE linea ${LINENO}: ${BASH_COMMAND}" >&2' ERR inspect_command() { check_source emit status OK emit source_root "$SOURCE_ROOT" emit source_database "$SOURCE_DB" emit source_domain "$SOURCE_DOMAIN" emit source_database_objects "$SOURCE_OBJECTS" emit application_database_access OK emit bower_target "$(readlink -f "$SOURCE_ROOT/bower_components")" emit node_target "$(readlink -f "$SOURCE_ROOT/node_modules")" } plan_command() { validate_slug build_names check_source check_target_absent emit status READY emit slug "$SLUG" emit domain "$TARGET_DOMAIN" emit document_root "$TARGET_ROOT" emit database "$TARGET_DB" emit source_database_objects "$SOURCE_OBJECTS" emit vhost "$TARGET_VHOST" } create_command() { [[ ${EUID} -eq 0 ]] || { echo "ERRORE: create richiede root." >&2; exit 50; } validate_slug build_names check_source check_target_absent mkdir -p "$(dirname "$LOCK_FILE")" "$(dirname "$LOG_FILE")" exec 9>"$LOCK_FILE" flock -n 9 || { echo "ERRORE: un'altra generazione è in corso." >&2; exit 51; } STAGE_ROOT="$(mktemp -d "${WEB_ROOT}/.dvm-generator.${SLUG}.XXXXXX")" log_event "START slug=${SLUG} domain=${TARGET_DOMAIN} database=${TARGET_DB}" "$RSYNC_BIN" -aH \ --exclude='/bower_components' \ --exclude='/node_modules' \ "$SOURCE_ROOT/" "$STAGE_ROOT/" rm -rf -- "$STAGE_ROOT/bower_components" "$STAGE_ROOT/node_modules" ln -s "$(readlink "$SOURCE_ROOT/bower_components")" "$STAGE_ROOT/bower_components" ln -s "$(readlink "$SOURCE_ROOT/node_modules")" "$STAGE_ROOT/node_modules" while IFS= read -r runtime_dir; do clear_runtime_dir "$STAGE_ROOT" "$runtime_dir" done <<'RUNTIME_DIRS' templates_c log libs/stimulsoft_legacy/tmp libs/mpdf/tmp libs/pdo/classes/library/tcpdf/cache theme/php/uploads webservices_report/log webServiceTime/classes/library/tcpdf/cache components/templates/custom_templates/ax_attendance/ax_schede_presenze_modA/timeCtrl_lastonsilvelox/log components/templates/custom_templates/ax_attendance/ax_schede_presenze_modA/timeCtrl_silvelox_ok/log components/templates/custom_templates/ax_attendance/ax_time_controll/timeCtrl/log components/templates/custom_templates/ax_attendance/impostazioni-generali/ctrl/upload RUNTIME_DIRS patch_database_name "$STAGE_ROOT/phpgen_settings.php" "$MYSQL_BIN" -e "CREATE DATABASE \`${TARGET_DB}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" CREATED_DB=1 "$MYSQLDUMP_BIN" --default-character-set=utf8mb4 --routines --events --triggers --hex-blob "$SOURCE_DB" \ | "$MYSQL_BIN" --default-character-set=utf8mb4 "$TARGET_DB" TARGET_OBJECTS="$(database_object_count "$TARGET_DB")" [[ "$TARGET_OBJECTS" == "$SOURCE_OBJECTS" ]] || { echo "ERRORE: inventario database non coincidente: sorgente=${SOURCE_OBJECTS}, destinazione=${TARGET_OBJECTS}" >&2 return 1 } application_db_access "$STAGE_ROOT/phpgen_settings.php" "$TARGET_DB" mv -- "$STAGE_ROOT" "$TARGET_ROOT" STAGE_ROOT="" CREATED_ROOT=1 render_vhost "$TARGET_VHOST" chown root:root "$TARGET_VHOST" chmod 0644 "$TARGET_VHOST" CREATED_VHOST=1 "$A2ENSITE_BIN" "$TARGET_VHOST_NAME" >/dev/null ENABLED_SITE=1 "$APACHECTL_BIN" configtest "$APACHECTL_BIN" graceful [[ -d "$TARGET_ROOT" ]] [[ -L "$TARGET_ROOT/bower_components" ]] [[ -L "$TARGET_ROOT/node_modules" ]] [[ -f "$TARGET_VHOST" ]] database_exists "$TARGET_DB" application_db_access "$TARGET_ROOT/phpgen_settings.php" "$TARGET_DB" local_http_check emit status CREATED emit slug "$SLUG" emit domain "$TARGET_DOMAIN" emit document_root "$TARGET_ROOT" emit database "$TARGET_DB" emit source_database_objects "$SOURCE_OBJECTS" emit target_database_objects "$TARGET_OBJECTS" emit application_database_access OK emit local_http_status "$HTTP_STATUS" emit vhost "$TARGET_VHOST" log_event "SUCCESS slug=${SLUG} domain=${TARGET_DOMAIN} database=${TARGET_DB} objects=${TARGET_OBJECTS} http=${HTTP_STATUS}" } case "$COMMAND" in -h|--help|help|'') usage exit 0 ;; inspect) validate_server inspect_command ;; plan) validate_server plan_command ;; create) validate_server create_command ;; *) echo "ERRORE: comando sconosciuto: $COMMAND" >&2 usage >&2 exit 2 ;; esac WORKER chmod 0750 "$CANDIDATE" bash -n "$CANDIDATE" { echo "STEP: $STEP_NAME" echo "Server: $HOST_NOW" echo "IP: $IPS_NOW" echo "Worker: $WORKER_PATH" echo "Base: $SOURCE_ROOT" echo "Database base: $SOURCE_DB" echo "Vhost base: $SOURCE_VHOST" echo "Modalità: patch motore; nessun portale reale creato" } > "${REPORT_DIR}/01_ENVIRONMENT.txt" printf 'Sintassi candidate: OK\n' > "${REPORT_DIR}/02_CANDIDATE_SYNTAX.txt" SIM="${WORKDIR}/simulation" mkdir -p "$SIM/web/presenze.distribuite" "$SIM/shared/bower_components" "$SIM/shared/node_modules" \ "$SIM/sites-available" "$SIM/sites-enabled" "$SIM/bin" "$SIM/state" cat > "$SIM/web/presenze.distribuite/phpgen_settings.php" <<'PHPSETTINGS' array( 'host' => 'localhost', 'username' => 'presenze_local', 'password' => 'MettiQuiPasswordForte', 'database' => 'presenze_distribuite', ), ); PHPSETTINGS printf '%s\n' ' "$SIM/web/presenze.distribuite/index.php" mkdir -p "$SIM/web/presenze.distribuite/templates_c" "$SIM/web/presenze.distribuite/log" printf 'runtime-da-pulire\n' > "$SIM/web/presenze.distribuite/templates_c/old.tmp" ln -s "$SIM/shared/bower_components" "$SIM/web/presenze.distribuite/bower_components" ln -s "$SIM/shared/node_modules" "$SIM/web/presenze.distribuite/node_modules" cat > "$SIM/sites-available/presenze.distribuite.axuno.it.conf" < ServerName presenze.distribuite.axuno.it DocumentRoot $SIM/web/presenze.distribuite Require all granted EOF : > "$SIM/state/db_presenze_distribuite" printf '87\n' > "$SIM/state/objects_presenze_distribuite" cat > "$SIM/bin/mysql" <<'FAKEMYSQL' #!/usr/bin/env bash set -Eeuo pipefail STATE="${FAKE_STATE:?}" query="" dbarg="" args=("$@") for ((i=0; i<${#args[@]}; i++)); do case "${args[$i]}" in -e) query="${args[$((i+1))]}" ((i+=1)) ;; --host=*|--user=*|--batch|--skip-column-names|--default-character-set=*) ;; -*) ;; *) dbarg="${args[$i]}" ;; esac done if [[ -n "$query" ]]; then if [[ "$query" == *"SELECT SCHEMA_NAME"* ]]; then db="$(sed -nE "s/.*SCHEMA_NAME='([^']+)'.*/\1/p" <<< "$query")" [[ -f "$STATE/db_$db" ]] && printf '%s\n' "$db" elif [[ "$query" == *"SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES"* ]]; then db="$(sed -nE "s/.*TABLE_SCHEMA='([^']+)'.*/\1/p" <<< "$query")" cat "$STATE/objects_$db" elif [[ "$query" == "SELECT 1" ]]; then [[ -n "$dbarg" && -f "$STATE/db_$dbarg" ]] || exit 1 [[ ! -f "$STATE/deny_$dbarg" ]] || exit 1 printf '1\n' elif [[ "$query" == CREATE\ DATABASE* ]]; then db="$(sed -nE 's/CREATE DATABASE `([^`]+)`.*/\1/p' <<< "$query")" : > "$STATE/db_$db" printf '0\n' > "$STATE/objects_$db" elif [[ "$query" == DROP\ DATABASE* ]]; then db="$(sed -nE 's/DROP DATABASE IF EXISTS `([^`]+)`.*/\1/p' <<< "$query")" rm -f "$STATE/db_$db" "$STATE/objects_$db" "$STATE/deny_$db" else echo "Query fake non gestita: $query" >&2 exit 2 fi else [[ -n "$dbarg" ]] || exit 2 cat >/dev/null : > "$STATE/db_$dbarg" cp "$STATE/objects_presenze_distribuite" "$STATE/objects_$dbarg" fi FAKEMYSQL cat > "$SIM/bin/mysqldump" <<'FAKEDUMP' #!/usr/bin/env bash printf '%s\n' '-- fake dump' FAKEDUMP cat > "$SIM/bin/rsync" <<'FAKERSYNC' #!/usr/bin/env bash set -Eeuo pipefail args=("$@") src="${args[$((${#args[@]}-2))]}" dst="${args[$((${#args[@]}-1))]}" cp -a "${src%/}/." "${dst%/}/" FAKERSYNC cat > "$SIM/bin/a2ensite" <<'FAKEA2EN' #!/usr/bin/env bash set -Eeuo pipefail ln -s "$FAKE_SITES_AVAILABLE/$1" "$FAKE_SITES_ENABLED/$1" FAKEA2EN cat > "$SIM/bin/a2dissite" <<'FAKEA2DIS' #!/usr/bin/env bash set -Eeuo pipefail rm -f "$FAKE_SITES_ENABLED/$1" FAKEA2DIS cat > "$SIM/bin/apache2ctl" <<'FAKEAPACHE' #!/usr/bin/env bash set -Eeuo pipefail [[ ! -f "$FAKE_STATE/apache_fail" ]] FAKEAPACHE cat > "$SIM/bin/curl" <<'FAKECURL' #!/usr/bin/env bash set -Eeuo pipefail out="" hdr="" mode="${FAKE_CURL_MODE:-ok}" args=("$@") for ((i=0; i<${#args[@]}; i++)); do case "${args[$i]}" in --output) out="${args[$((i+1))]}" ((i+=1)) ;; --dump-header) hdr="${args[$((i+1))]}" ((i+=1)) ;; esac done case "$mode" in ok) printf 'HTTP/1.1 200 OK\r\n\r\n' > "$hdr" printf 'OK\n' > "$out" printf '200' ;; fatal) printf 'HTTP/1.1 200 OK\r\n\r\n' > "$hdr" printf 'Fatal error: test rollback\n' > "$out" printf '200' ;; *) exit 2 ;; esac FAKECURL chmod 0750 "$SIM/bin"/* COMMON_ENV=( DVM_GEN_TEST_MODE=1 DVM_GEN_SOURCE_ROOT="$SIM/web/presenze.distribuite" DVM_GEN_SOURCE_DB=presenze_distribuite DVM_GEN_SOURCE_DOMAIN=presenze.distribuite.axuno.it DVM_GEN_WEB_ROOT="$SIM/web" DVM_GEN_SITES_AVAILABLE="$SIM/sites-available" DVM_GEN_SITES_ENABLED="$SIM/sites-enabled" DVM_GEN_SOURCE_VHOST="$SIM/sites-available/presenze.distribuite.axuno.it.conf" DVM_GEN_LOCK_FILE="$SIM/generator.lock" DVM_GEN_LOG_FILE="$SIM/generator.log" DVM_GEN_MYSQL_BIN="$SIM/bin/mysql" DVM_GEN_MYSQLDUMP_BIN="$SIM/bin/mysqldump" DVM_GEN_APACHECTL_BIN="$SIM/bin/apache2ctl" DVM_GEN_A2ENSITE_BIN="$SIM/bin/a2ensite" DVM_GEN_A2DISSITE_BIN="$SIM/bin/a2dissite" DVM_GEN_RSYNC_BIN="$SIM/bin/rsync" DVM_GEN_CURL_BIN="$SIM/bin/curl" FAKE_STATE="$SIM/state" FAKE_SITES_AVAILABLE="$SIM/sites-available" FAKE_SITES_ENABLED="$SIM/sites-enabled" ) env "${COMMON_ENV[@]}" "$CANDIDATE" inspect > "${REPORT_DIR}/03_SIMULATION_INSPECT.txt" env "${COMMON_ENV[@]}" "$CANDIDATE" plan simulazione-ok > "${REPORT_DIR}/04_SIMULATION_PLAN.txt" env "${COMMON_ENV[@]}" "$CANDIDATE" create simulazione-ok > "${REPORT_DIR}/05_SIMULATION_CREATE.txt" [[ -d "$SIM/web/presenze.simulazione-ok" ]] [[ -L "$SIM/web/presenze.simulazione-ok/bower_components" ]] [[ -L "$SIM/web/presenze.simulazione-ok/node_modules" ]] [[ ! -e "$SIM/web/presenze.simulazione-ok/templates_c/old.tmp" ]] [[ -f "$SIM/state/db_presenze_simulazione_ok" ]] [[ -L "$SIM/sites-enabled/presenze.simulazione-ok.axuno.it.conf" ]] grep -Fq "'database' => 'presenze_simulazione_ok'" "$SIM/web/presenze.simulazione-ok/phpgen_settings.php" grep -Fq 'target_database_objects=87' "${REPORT_DIR}/05_SIMULATION_CREATE.txt" grep -Fq 'application_database_access=OK' "${REPORT_DIR}/05_SIMULATION_CREATE.txt" grep -Fq 'local_http_status=200' "${REPORT_DIR}/05_SIMULATION_CREATE.txt" trap - ERR set +e FAKE_CURL_MODE=fatal env "${COMMON_ENV[@]}" "$CANDIDATE" create simulazione-rollback \ > "${REPORT_DIR}/06_SIMULATION_ROLLBACK.stdout" \ 2> "${REPORT_DIR}/06_SIMULATION_ROLLBACK.stderr" ROLLBACK_RC=$? set -e trap 'log "ERRORE linea ${LINENO}: ${BASH_COMMAND}"' ERR [[ $ROLLBACK_RC -ne 0 ]] [[ ! -e "$SIM/web/presenze.simulazione-rollback" ]] [[ ! -e "$SIM/state/db_presenze_simulazione_rollback" ]] [[ ! -e "$SIM/sites-available/presenze.simulazione-rollback.axuno.it.conf" ]] [[ ! -e "$SIM/sites-enabled/presenze.simulazione-rollback.axuno.it.conf" ]] grep -Fq 'ROLLBACK: ripristino dopo errore.' "${REPORT_DIR}/06_SIMULATION_ROLLBACK.stderr" install -o root -g root -m 0750 "$CANDIDATE" "$WORKER_PATH" INSTALLED_NEW=1 bash -n "$WORKER_PATH" "$WORKER_PATH" inspect > "${REPORT_DIR}/07_INSTALLED_INSPECT.txt" "$WORKER_PATH" plan "$PLAN_SLUG" > "${REPORT_DIR}/08_INSTALLED_PLAN.txt" grep -Fq 'status=OK' "${REPORT_DIR}/07_INSTALLED_INSPECT.txt" grep -Fq 'application_database_access=OK' "${REPORT_DIR}/07_INSTALLED_INSPECT.txt" grep -Fq 'status=READY' "${REPORT_DIR}/08_INSTALLED_PLAN.txt" { echo "Worker precedente: salvato e ripristinabile durante lo step" echo "Worker installato: $WORKER_PATH" echo "Owner/mode: $(stat -c '%U:%G %a' "$WORKER_PATH")" echo "SHA256: $(sha256sum "$WORKER_PATH" | awk '{print $1}')" } > "${REPORT_DIR}/09_INSTALLATION.txt" { echo "Verifica sintassi installer: OK" echo "Verifica sintassi worker candidato: OK" echo "Simulazione inspect: OK" echo "Simulazione plan: OK" echo "Simulazione create completa: OK" echo "Verifica copia database per numero oggetti: OK" echo "Verifica accesso database con credenziali applicative: OK" echo "Verifica HTTP locale: OK" echo "Simulazione errore HTTP e rollback completo: OK" echo "Worker installato e riesaminato: OK" echo "Inspect reale della base: OK" echo "Plan reale senza modifiche: OK" echo "Portali reali creati: 0" echo "Database reali creati: 0" echo "Vhost reali creati/abilitati: 0" echo "Apache reale ricaricato: NO" echo "Silvelox toccato: NO" echo "Stato: GENERATOR_DEEP_VALIDATION_READY" } > "${REPORT_DIR}/99_SUMMARY.txt" cp -- "$WORKER_PATH" "${REPORT_DIR}/dvm-presenze-portal-generator.txt" ( cd "$REPORT_DIR" zip -q -r "$ZIP_PATH" . ) zip -T "$ZIP_PATH" >/dev/null FINISHED=1 log "PATCH COMPLETATA" log "Worker: $WORKER_PATH" log "Nessun portale reale creato." log "Output: $ZIP_PATH"