#!/usr/bin/env bash set -euo pipefail usage() { cat <<'USAGE' Usage: run_verification_only.sh \ --source-run-dir \ --source-state-root \ --out-dir \ [--rpki-bin ] [--keep-scratch] validation time is always read from the source CIR. The wrapper serializes access with normal soak runs and never writes into the normal run sequence. USAGE } SOURCE_RUN_DIR="" SOURCE_STATE_ROOT="" OUT_DIR="" RPKI_BIN="" KEEP_SCRATCH=0 while [[ $# -gt 0 ]]; do case "$1" in --source-run-dir) SOURCE_RUN_DIR="${2:?missing value}"; shift 2 ;; --source-state-root) SOURCE_STATE_ROOT="${2:?missing value}"; shift 2 ;; --out-dir) OUT_DIR="${2:?missing value}"; shift 2 ;; --rpki-bin) RPKI_BIN="${2:?missing value}"; shift 2 ;; --keep-scratch) KEEP_SCRATCH=1; shift ;; -h|--help) usage; exit 0 ;; *) echo "unknown argument: $1" >&2; usage >&2; exit 2 ;; esac done [[ -n "$SOURCE_RUN_DIR" ]] || { echo "--source-run-dir is required" >&2; exit 2; } [[ -n "$SOURCE_STATE_ROOT" ]] || { echo "--source-state-root is required" >&2; exit 2; } [[ -n "$OUT_DIR" ]] || { echo "--out-dir is required" >&2; exit 2; } if [[ -z "$RPKI_BIN" ]]; then RPKI_BIN="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)/target/release/rpki" fi [[ -x "$RPKI_BIN" ]] || { echo "rpki binary is not executable: $RPKI_BIN" >&2; exit 2; } command -v flock >/dev/null || { echo "flock is required" >&2; exit 2; } command -v python3 >/dev/null || { echo "python3 is required" >&2; exit 2; } now_ms() { python3 -c 'import time; print(time.time_ns() // 1_000_000)' } mkdir -p "$SOURCE_STATE_ROOT/locks" "$(dirname "$OUT_DIR")" [[ ! -e "$OUT_DIR" ]] || { echo "verification output already exists: $OUT_DIR" >&2; exit 2; } LOCK_PATH="$SOURCE_STATE_ROOT/locks/state-execution.lock" STDOUT_TMP="${OUT_DIR}.stdout.tmp" STDERR_TMP="${OUT_DIR}.stderr.tmp" PROCESS_TIME_TMP="${OUT_DIR}.process-time.tmp" WAIT_STARTED_MS="$(now_ms)" exec {LOCK_FD}>"$LOCK_PATH" flock "$LOCK_FD" LOCK_ACQUIRED_MS="$(now_ms)" ARGS=( --verification-only --verification-source-run "$SOURCE_RUN_DIR" --verification-source-state "$SOURCE_STATE_ROOT" --verification-out "$OUT_DIR" ) if [[ "$KEEP_SCRATCH" -eq 1 ]]; then ARGS+=(--keep-verification-scratch) fi set +e if [[ -x /usr/bin/time ]]; then RPKI_STATE_EXECUTION_LOCK_HELD=1 /usr/bin/time -v -o "$PROCESS_TIME_TMP" -- \ "$RPKI_BIN" "${ARGS[@]}" >"$STDOUT_TMP" 2>"$STDERR_TMP" else RPKI_STATE_EXECUTION_LOCK_HELD=1 "$RPKI_BIN" "${ARGS[@]}" >"$STDOUT_TMP" 2>"$STDERR_TMP" fi EXIT_CODE=$? set -e LOCK_RELEASED_MS="$(now_ms)" flock -u "$LOCK_FD" exec {LOCK_FD}>&- mkdir -p "$OUT_DIR" mv "$STDOUT_TMP" "$OUT_DIR/stdout.log" mv "$STDERR_TMP" "$OUT_DIR/stderr.log" if [[ -f "$PROCESS_TIME_TMP" ]]; then mv "$PROCESS_TIME_TMP" "$OUT_DIR/process-time.txt" fi python3 - "$OUT_DIR/wrapper-timing.json" "$WAIT_STARTED_MS" "$LOCK_ACQUIRED_MS" "$LOCK_RELEASED_MS" "$EXIT_CODE" <<'PY' import json import sys from pathlib import Path path = Path(sys.argv[1]) wait_started_ms, lock_acquired_ms, lock_released_ms, exit_code = map(int, sys.argv[2:]) path.write_text(json.dumps({ "lockWaitMs": lock_acquired_ms - wait_started_ms, "lockHeldMs": lock_released_ms - lock_acquired_ms, "exitCode": exit_code, }, indent=2, sort_keys=True) + "\n", encoding="utf-8") PY exit "$EXIT_CODE"