#!/usr/bin/env bash set -u EXPECTED_S03_CONFIG_SHA256="0f8916735ff67a9be2b167b52f147687fe131a063f410a81bfbf3bf9ff65f1e4" MINIMUM_SAMPLES="50" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PARSER="$SCRIPT_DIR/parse_s03_binary.py" FUNCTIONAL_STATUS="NOT_DEMONSTRATED" SEMANTIC_STATUS="NOT_DEMONSTRATED" TIMING_STATUS="NOT_EVALUATED" MEASUREMENT_ELIGIBILITY="NO" emit_contract() { printf "FUNCTIONAL_STATUS=%s\n" "$FUNCTIONAL_STATUS" printf "SEMANTIC_STATUS=%s\n" "$SEMANTIC_STATUS" printf "TIMING_STATUS=%s\n" "$TIMING_STATUS" printf "MEASUREMENT_ELIGIBILITY=%s\n" "$MEASUREMENT_ELIGIBILITY" } fail_early() { code="$1" shift printf "[NON DIMOSTRATO] %s\n" "$*" >&2 emit_contract printf "VALIDATION_RESULT=FAIL_REQUIRED_INPUT\n" exit "$code" } if [ "$#" -ne 1 ]; then printf "Uso: %s \n" "$0" >&2 emit_contract exit 2 fi INPUT_PATH="$1" if [ -d "$INPUT_PATH" ]; then RUN_DIR="$(cd "$INPUT_PATH" && pwd)" RUN_LOG="$RUN_DIR/run.log" elif [ -f "$INPUT_PATH" ]; then RUN_LOG="$(cd "$(dirname "$INPUT_PATH")" && pwd)/$(basename "$INPUT_PATH")" RUN_DIR="$(dirname "$RUN_LOG")" else fail_early 21 "Percorso di validazione non trovato: $INPUT_PATH" fi RUN_CONFIG="$RUN_DIR/configuration.marte" OUTPUT_FILE="$RUN_DIR/s03_output.bin" RUNNER_SUMMARY="$RUN_DIR/runner_summary.txt" RUN_HASHES="$RUN_DIR/hashes.sha256" if [ ! -x "$PARSER" ]; then fail_early 21 "Parser S03 assente o non eseguibile: $PARSER" fi for required_file in \ "$RUN_LOG" \ "$RUN_CONFIG" \ "$OUTPUT_FILE" \ "$RUNNER_SUMMARY" \ "$RUN_HASHES" do if [ ! -f "$required_file" ]; then fail_early 21 "File richiesto assente: $required_file" fi done if [ ! -s "$RUN_HASHES" ]; then fail_early 21 "Manifest hash assente o vuoto: $RUN_HASHES" fi count_lines() { pattern="$1" file="$2" grep -Ec "$pattern" "$file" || true } START_LINES="$(count_lines '^\[Information - RealTimeLoader\.cpp:[0-9]+\]: Started application in state State1[[:space:]]*$' "$RUN_LOG")" APPLICATION_START_LINES="$(count_lines '^\[Information - MARTeApp\.cpp:[0-9]+\]: Application starting[[:space:]]*$' "$RUN_LOG")" FILE_OPEN_LINES="$(count_lines '^\[Information - FileWriter\.cpp:[0-9]+\]: Going to open file with name ' "$RUN_LOG")" SIGINT_LINES="$(count_lines '^\[Information - Bootstrap\.cpp:[0-9]+\]: Application recieved SIGINT\.[[:space:]]*$' "$RUN_LOG")" STOP_OK_LINES="$(count_lines '^\[Information - Bootstrap\.cpp:[0-9]+\]: Application successfully stopped\.[[:space:]]*$' "$RUN_LOG")" TERMINATED_LINES="$(count_lines '^\[NoError - MARTeApp\.cpp:[0-9]+\]: Application terminated[[:space:]]*$' "$RUN_LOG")" STARTUP_ERROR_LINES="$(count_lines 'Failed dlopen|Failed CreateByName|Failed to Initialise object|Failed to initialise the ObjectRegistryDatabase|Could not Initialise the loader' "$RUN_LOG")" FATAL_ERROR_LINES="$(count_lines '^\[FatalError ' "$RUN_LOG")" OS_ERROR_LINES="$(count_lines '^\[OSError ' "$RUN_LOG")" ERROR_LINES="$(count_lines '^\[Error ' "$RUN_LOG")" INITIALISATION_ERROR_LINES="$(count_lines '^\[InitialisationEr ' "$RUN_LOG")" WARNING_LINES="$(count_lines '^\[Warning ' "$RUN_LOG")" PRIORITY_CLIP_LINES="$(count_lines '^\[Warning - Threads\.cpp:[0-9]+\]: Requested a thread priority that is higher than ' "$RUN_LOG")" PRIORITY_FAIL_LINES="$(count_lines '^\[Warning - Threads\.cpp:[0-9]+\]: Failed to change the thread priority' "$RUN_LOG")" LINUX_TIMER_DEFAULT_WARNING_LINES="$(count_lines '^\[Warning - LinuxTimer\.cpp:[0-9]+\]: (ExecutionMode|CPUMask|StackSize) not specified using: ' "$RUN_LOG")" CONFIG_DB_NODE_WARNING_LINES="$(count_lines '^\[Warning - MARTeApp\.cpp:[0-9]+\]: \[ConfigurationDatabaseNode\] - instances: [0-9]+[[:space:]]*$' "$RUN_LOG")" UNCLASSIFIED_WARNING_LINES=$(( WARNING_LINES - PRIORITY_CLIP_LINES - PRIORITY_FAIL_LINES - LINUX_TIMER_DEFAULT_WARNING_LINES - CONFIG_DB_NODE_WARNING_LINES )) if [ "$UNCLASSIFIED_WARNING_LINES" -lt 0 ]; then UNCLASSIFIED_WARNING_LINES=0 fi DOUBLE_STOP_LINES="$(count_lines '^\[FatalError - RealTimeApplication\.cpp:[0-9]+\]: Could not stop the RealTimeApplication\. Was it ever started\?[[:space:]]*$' "$RUN_LOG")" ASYNC_EVENTSEM_CLOSE_LINES="$(count_lines '^\[FatalError - MemoryMapAsyncOutputBroker\.cpp:[0-9]+\]: Could not Close the EventSem\.[[:space:]]*$' "$RUN_LOG")" PTHREAD_MUTEX_DESTROY_LINES="$(count_lines '^\[OSError - EventSem\.cpp:[0-9]+\]: Error: pthread_mutex_destroy\(\)[[:space:]]*$' "$RUN_LOG")" UNCLASSIFIED_FATAL_LINES=$((FATAL_ERROR_LINES - DOUBLE_STOP_LINES - ASYNC_EVENTSEM_CLOSE_LINES)) UNCLASSIFIED_OS_ERROR_LINES=$((OS_ERROR_LINES - PTHREAD_MUTEX_DESTROY_LINES)) if [ "$UNCLASSIFIED_FATAL_LINES" -lt 0 ]; then UNCLASSIFIED_FATAL_LINES=0 fi if [ "$UNCLASSIFIED_OS_ERROR_LINES" -lt 0 ]; then UNCLASSIFIED_OS_ERROR_LINES=0 fi CONFIG_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03_validator_config_XXXXXX.log")" || exit 22 BINARY_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03_validator_binary_XXXXXX.log")" || exit 22 HASH_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03_validator_hashes_XXXXXX.log")" || exit 22 trap 'rm -f -- "$CONFIG_REPORT" "$BINARY_REPORT" "$HASH_REPORT"' EXIT HUP INT TERM EXPECTED_FILENAME="$RUN_DIR/s03_output.bin" PYTHONHASHSEED=0 python3 - \ "$RUN_CONFIG" \ "$EXPECTED_FILENAME" \ "$EXPECTED_S03_CONFIG_SHA256" \ >"$CONFIG_REPORT" 2>&1 <<'PY_S03_CONFIG' import hashlib import sys from pathlib import Path config_path = Path(sys.argv[1]) expected_filename = sys.argv[2] expected_hash = sys.argv[3] text = config_path.read_text(encoding="utf-8") expected_line = f' Filename = "{expected_filename}"' placeholder_line = ' Filename = "__MARTE_RUN_DIR__/s03_output.bin"' filename_count = sum(1 for line in text.splitlines() if line == expected_line) placeholder_count = text.count("__MARTE_RUN_DIR__") canonical = text.replace(expected_line, placeholder_line, 1) canonical_hash = hashlib.sha256(canonical.encode("utf-8")).hexdigest() checks = { "EXPECTED_FILENAME_COUNT": filename_count, "PLACEHOLDER_COUNT": placeholder_count, "FILEWRITER_CLASS_COUNT": text.count("Class = FileDataSource::FileWriter"), "PLAIN_FILEWRITER_CLASS_COUNT": text.count("Class = FileWriter"), "CONSTANT_GAM_COUNT": text.count("Class = ConstantGAM"), "SENTINEL_SCALAR_A_DECLARATION_COUNT": text.count("SentinelScalarA ="), "SENTINEL_SCALAR_B_DECLARATION_COUNT": text.count("SentinelScalarB ="), "SENTINEL_VECTOR_DECLARATION_COUNT": text.count("SentinelVector ="), "VECTOR_DIMENSION_COUNT": text.count("NumberOfDimensions = 1"), "VECTOR_CARDINALITY_COUNT": text.count("NumberOfElements = 3"), "THREAD_ORDER_COUNT": text.count( "Functions = {GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}" ), } expected_checks = { "EXPECTED_FILENAME_COUNT": 1, "PLACEHOLDER_COUNT": 0, "FILEWRITER_CLASS_COUNT": 1, "PLAIN_FILEWRITER_CLASS_COUNT": 0, "CONSTANT_GAM_COUNT": 1, "SENTINEL_SCALAR_A_DECLARATION_COUNT": 4, "SENTINEL_SCALAR_B_DECLARATION_COUNT": 4, "SENTINEL_VECTOR_DECLARATION_COUNT": 4, "VECTOR_DIMENSION_COUNT": 4, "VECTOR_CARDINALITY_COUNT": 4, "THREAD_ORDER_COUNT": 1, } for key in sorted(checks): print(f"{key}={checks[key]}") print(f"CANONICAL_CONFIG_SHA256={canonical_hash}") print(f"EXPECTED_CONFIG_SHA256={expected_hash}") if checks == expected_checks and canonical_hash == expected_hash: print("CONFIG_STATUS=PASS") sys.exit(0) print("CONFIG_STATUS=FAIL") sys.exit(1) PY_S03_CONFIG CONFIG_RC=$? PYTHONHASHSEED=0 "$PARSER" "$OUTPUT_FILE" --min-samples "$MINIMUM_SAMPLES" \ >"$BINARY_REPORT" 2>&1 PARSER_RC=$? sha256sum -c --strict "$RUN_HASHES" >"$HASH_REPORT" 2>&1 HASH_RC=$? report_value() { key="$1" file="$2" sed -n "s/^${key}=//p" "$file" | tail -n 1 } CONFIG_STATUS="$(report_value CONFIG_STATUS "$CONFIG_REPORT")" FUNCTIONAL_BINARY_STATUS="$(report_value FUNCTIONAL_BINARY_STATUS "$BINARY_REPORT")" SEMANTIC_BINARY_STATUS="$(report_value SEMANTIC_BINARY_STATUS "$BINARY_REPORT")" BINARY_STATUS="$(report_value BINARY_STATUS "$BINARY_REPORT")" FIRST_ERROR_CODE="$(report_value FIRST_ERROR_CODE "$BINARY_REPORT")" COMPLETE_SAMPLE_COUNT="$(report_value COMPLETE_SAMPLE_COUNT "$BINARY_REPORT")" HEADER_BYTES="$(report_value OBSERVED_HEADER_BYTES "$BINARY_REPORT")" RECORD_BYTES="$(report_value OBSERVED_RECORD_BYTES_FROM_HEADER "$BINARY_REPORT")" PAYLOAD_BYTES="$(report_value PAYLOAD_BYTES "$BINARY_REPORT")" PAYLOAD_REMAINDER_BYTES="$(report_value PAYLOAD_REMAINDER_BYTES "$BINARY_REPORT")" MARTE_EXIT_CODE="$(sed -n 's/^Exit code MARTe:[[:space:]]*//p' "$RUNNER_SUMMARY" | tail -n 1)" TEE_EXIT_CODE="$(sed -n 's/^Exit code tee:[[:space:]]*//p' "$RUNNER_SUMMARY" | tail -n 1)" if [ -z "$MARTE_EXIT_CODE" ]; then MARTE_EXIT_CODE="NOT_AVAILABLE" fi if [ -z "$TEE_EXIT_CODE" ]; then TEE_EXIT_CODE="NOT_AVAILABLE" fi case "$MARTE_EXIT_CODE" in 137) TIMING_STATUS="NOT_VALID_EXIT_137_SIGKILL" ;; *) if [ "$PRIORITY_FAIL_LINES" -ge 1 ]; then TIMING_STATUS="NOT_VALID_PRIORITY_NOT_APPLIED" else TIMING_STATUS="UNASSESSED_DT_SEMANTICS" fi ;; esac FUNCTIONAL_OK=0 if [ "$START_LINES" -ge 1 ] && [ "$APPLICATION_START_LINES" -ge 1 ] && [ "$FILE_OPEN_LINES" -ge 1 ] && [ "$STARTUP_ERROR_LINES" -eq 0 ] && [ "$CONFIG_RC" -eq 0 ] && [ "$CONFIG_STATUS" = "PASS" ] && [ "$FUNCTIONAL_BINARY_STATUS" = "PASS" ] && [ "$HASH_RC" -eq 0 ] && [ "$TEE_EXIT_CODE" = "0" ] && [ "$UNCLASSIFIED_FATAL_LINES" -eq 0 ] && [ "$UNCLASSIFIED_OS_ERROR_LINES" -eq 0 ] && [ "$ERROR_LINES" -eq 0 ] && [ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then FUNCTIONAL_OK=1 fi if [ "$FUNCTIONAL_OK" -eq 1 ]; then FUNCTIONAL_STATUS="PASS" fi if [ "$CONFIG_STATUS" = "PASS" ] && [ "$SEMANTIC_BINARY_STATUS" = "PASS" ]; then SEMANTIC_STATUS="PASS" elif [ "$SEMANTIC_BINARY_STATUS" = "FAIL" ]; then SEMANTIC_STATUS="FAIL" else SEMANTIC_STATUS="NOT_DEMONSTRATED" fi printf "============================================================\n" printf "VALIDAZIONE S03 - SENTINELLE SEMANTICHE\n" printf "============================================================\n" printf "Run directory: %s\n" "$RUN_DIR" printf "Configurazione: %s\n" "$RUN_CONFIG" printf "File binario: %s\n" "$OUTPUT_FILE" printf "Manifest hash: %s\n" "$RUN_HASHES" printf "Configurazione canonica: %s (rc=%s)\n" "${CONFIG_STATUS:-NOT_AVAILABLE}" "$CONFIG_RC" printf "Verifica manifest hash: %s (rc=%s)\n" "$( [ "$HASH_RC" -eq 0 ] && printf PASS || printf FAIL )" "$HASH_RC" printf "Parser binario: %s (rc=%s)\n" "${BINARY_STATUS:-NOT_AVAILABLE}" "$PARSER_RC" printf "Primo errore parser: %s\n" "${FIRST_ERROR_CODE:-NOT_AVAILABLE}" printf "Header osservato: %s byte\n" "${HEADER_BYTES:-NOT_AVAILABLE}" printf "Record da header: %s byte\n" "${RECORD_BYTES:-NOT_AVAILABLE}" printf "Payload: %s byte\n" "${PAYLOAD_BYTES:-NOT_AVAILABLE}" printf "Residuo payload: %s byte\n" "${PAYLOAD_REMAINDER_BYTES:-NOT_AVAILABLE}" printf "Campioni completi: %s\n" "${COMPLETE_SAMPLE_COUNT:-NOT_AVAILABLE}" printf "Avvii State1: %s\n" "$START_LINES" printf "Application starting: %s\n" "$APPLICATION_START_LINES" printf "Aperture FileWriter: %s\n" "$FILE_OPEN_LINES" printf "Exit code MARTe: %s\n" "$MARTE_EXIT_CODE" printf "Exit code tee: %s\n" "$TEE_EXIT_CODE" printf "Warning priorita' non applic.: %s\n" "$PRIORITY_FAIL_LINES" printf "Warning non classificati: %s\n" "$UNCLASSIFIED_WARNING_LINES" printf "FatalError non classificati: %s\n" "$UNCLASSIFIED_FATAL_LINES" printf "OSError non classificati: %s\n" "$UNCLASSIFIED_OS_ERROR_LINES" printf "Error non classificati: %s\n" "$ERROR_LINES" printf "InitialisationError: %s\n" "$INITIALISATION_ERROR_LINES" printf "\n--- DETTAGLIO CONFIGURAZIONE ---\n" cat -- "$CONFIG_REPORT" printf "\n--- DETTAGLIO PARSER BINARIO ---\n" cat -- "$BINARY_REPORT" printf "\n--- VERIFICA HASH ---\n" cat -- "$HASH_REPORT" printf "\n--- CONTRATTO S03 ---\n" emit_contract if [ "$FUNCTIONAL_STATUS" != "PASS" ]; then printf "VALIDATION_RESULT=FUNCTIONAL_NOT_DEMONSTRATED\n" exit 20 fi if [ "$SEMANTIC_STATUS" != "PASS" ]; then printf "VALIDATION_RESULT=SEMANTIC_VALIDATION_FAILED\n" exit 20 fi if [ "$SIGINT_LINES" -ge 1 ] && [ "$STOP_OK_LINES" -ge 1 ] && [ "$TERMINATED_LINES" -ge 1 ] && [ "$FATAL_ERROR_LINES" -eq 0 ] && [ "$OS_ERROR_LINES" -eq 0 ] && [ "$ERROR_LINES" -eq 0 ] && [ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then printf "VALIDATION_RESULT=PASS_CLEAN_SHUTDOWN\n" exit 0 fi KNOWN_SHUTDOWN_TOTAL=$(( DOUBLE_STOP_LINES + ASYNC_EVENTSEM_CLOSE_LINES + PTHREAD_MUTEX_DESTROY_LINES )) if [ "$SIGINT_LINES" -ge 1 ] && [ "$STOP_OK_LINES" -ge 1 ] && [ "$TERMINATED_LINES" -ge 1 ] && [ "$KNOWN_SHUTDOWN_TOTAL" -ge 1 ] && [ "$UNCLASSIFIED_FATAL_LINES" -eq 0 ] && [ "$UNCLASSIFIED_OS_ERROR_LINES" -eq 0 ] && [ "$ERROR_LINES" -eq 0 ] && [ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then printf "VALIDATION_RESULT=PASS_KNOWN_SHUTDOWN_ANOMALY\n" exit 10 fi printf "VALIDATION_RESULT=PASS_UNCLASSIFIED_SHUTDOWN_ANOMALY\n" exit 11