Initial commit - Phase 3/4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
michael
2026-01-06 13:45:29 -08:00
parent 1da6730735
commit 4f35df1781
323 changed files with 98287 additions and 1195 deletions

657
scripts/preflight-check.sh Executable file
View File

@@ -0,0 +1,657 @@
#!/bin/bash
# Comprehensive Pre-Flight Validation for Dangerous Pi Builds
# Catches errors BEFORE wasting hours on failed builds
# Usage: ./scripts/preflight-check.sh
# Don't use set -e - we want to collect ALL errors, not stop on first one
set +e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
PI_GEN_DIR="/home/work/pi-gen-builder"
# Colors
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
ERRORS=0
WARNINGS=0
CHECKS_PASSED=0
error() {
echo -e "${RED}✗ ERROR:${NC} $1"
((ERRORS++))
}
warn() {
echo -e "${YELLOW}⚠ WARNING:${NC} $1"
((WARNINGS++))
}
pass() {
echo -e "${GREEN}${NC} $1"
((CHECKS_PASSED++))
}
info() {
echo -e "${BLUE}${NC} $1"
}
section() {
echo ""
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
}
echo ""
echo "╔════════════════════════════════════════════════════════════╗"
echo "║ DANGEROUS PI BUILD PRE-FLIGHT VALIDATION ║"
echo "╚════════════════════════════════════════════════════════════╝"
echo ""
# ============================================================================
section "1. PI-GEN ENVIRONMENT CHECKS"
# ============================================================================
# Check if pi-gen directory exists
if [ -d "$PI_GEN_DIR" ]; then
pass "Pi-gen directory exists: $PI_GEN_DIR"
else
error "Pi-gen directory not found: $PI_GEN_DIR"
fi
# Check Docker availability
if command -v docker &> /dev/null; then
if docker ps &> /dev/null; then
pass "Docker is available and accessible"
else
error "Docker command exists but cannot connect (check permissions)"
fi
else
error "Docker is not installed or not in PATH"
fi
# Check qemu registration
if [ -f "/proc/sys/fs/binfmt_misc/qemu-aarch64" ]; then
if grep -q "enabled" /proc/sys/fs/binfmt_misc/qemu-aarch64; then
pass "qemu-aarch64 is registered and enabled"
else
warn "qemu-aarch64 exists but may not be enabled"
fi
else
warn "qemu-aarch64 not registered (will be registered during build)"
fi
# ============================================================================
section "2. CONFIGURATION FILE VALIDATION"
# ============================================================================
CONFIG_FILE="$PROJECT_DIR/pi-gen/config"
if [ -f "$CONFIG_FILE" ]; then
pass "Config file exists: $CONFIG_FILE"
# Check required variables
for var in IMG_NAME RELEASE STAGE_LIST FIRST_USER_NAME; do
if grep -q "^$var=" "$CONFIG_FILE"; then
value=$(grep "^$var=" "$CONFIG_FILE" | cut -d'=' -f2- | tr -d '"')
pass "Config has $var=$value"
else
error "Config missing required variable: $var"
fi
done
# Check QCOW2 setting
if grep -q "USE_QCOW2=1" "$CONFIG_FILE"; then
pass "QCOW2 caching enabled (fast incremental builds)"
else
warn "QCOW2 caching disabled (slow rebuilds)"
fi
else
error "Config file not found: $CONFIG_FILE"
fi
# ============================================================================
section "3. STAGE STRUCTURE VALIDATION"
# ============================================================================
# Get list of custom stages from config
CUSTOM_STAGES=$(grep "^STAGE_LIST=" "$CONFIG_FILE" | cut -d'"' -f2 | grep -oE 'stage[A-Za-z0-9_]+' | grep -v '^stage[0-9]$')
for stage in $CUSTOM_STAGES; do
STAGE_DIR="$PROJECT_DIR/pi-gen/$stage"
if [ ! -d "$STAGE_DIR" ]; then
error "Stage directory does not exist: $STAGE_DIR"
continue
fi
info "Checking stage: $stage"
# Check for prerun.sh (CRITICAL - this was the bug!)
if [ -f "$STAGE_DIR/prerun.sh" ]; then
pass " Has prerun.sh"
# Check if prerun.sh contains copy_previous
if grep -q "copy_previous" "$STAGE_DIR/prerun.sh"; then
pass " prerun.sh calls copy_previous"
else
error " prerun.sh exists but doesn't call copy_previous"
info " This will cause: 'Unable to chroot' error"
fi
# Check if executable
if [ -x "$STAGE_DIR/prerun.sh" ]; then
pass " prerun.sh is executable"
else
warn " prerun.sh is not executable (should be chmod +x)"
fi
else
# Only error if this is not stage0 (stage0 doesn't need prerun.sh)
if [ "$stage" != "stage0" ]; then
error " MISSING prerun.sh (CRITICAL)"
info " This will cause: 'No such file or directory' for rootfs"
info " Fix: Create prerun.sh with copy_previous function"
fi
fi
# Check for EXPORT_IMAGE
if [ -f "$STAGE_DIR/EXPORT_IMAGE" ]; then
pass " Has EXPORT_IMAGE"
# Validate EXPORT_IMAGE syntax
if grep -q "IMG_SUFFIX" "$STAGE_DIR/EXPORT_IMAGE"; then
pass " EXPORT_IMAGE has IMG_SUFFIX"
else
warn " EXPORT_IMAGE missing IMG_SUFFIX"
fi
else
warn " No EXPORT_IMAGE (stage won't create image)"
fi
# Check for substages
substage_count=$(find "$STAGE_DIR" -maxdepth 1 -type d -name '[0-9][0-9]*' | wc -l)
if [ $substage_count -eq 0 ]; then
warn " No substages found (stage does nothing)"
else
pass " Found $substage_count substage(s)"
fi
done
# ============================================================================
section "4. BUILD SCRIPT SYNTAX VALIDATION"
# ============================================================================
info "Checking all stage scripts for syntax errors..."
for script in "$PROJECT_DIR"/pi-gen/stage*/*/[0-9][0-9]-*.sh; do
if [ -f "$script" ]; then
if bash -n "$script" 2>/dev/null; then
pass " $(basename $(dirname $(dirname "$script")))/$(basename $(dirname "$script"))/$(basename "$script")"
else
error " Syntax error in: $script"
bash -n "$script" 2>&1 | sed 's/^/ /'
fi
fi
done
# Check prerun.sh files
for script in "$PROJECT_DIR"/pi-gen/stage*/prerun.sh; do
if [ -f "$script" ]; then
if bash -n "$script" 2>/dev/null; then
pass " $(basename $(dirname "$script"))/prerun.sh"
else
error " Syntax error in: $script"
fi
fi
done
# ============================================================================
section "5. DEPENDENCY CHECKS IN SCRIPTS"
# ============================================================================
info "Checking if commands are installed before use..."
# Check PM3 stage for cmake before use
PM3_SCRIPT="$PROJECT_DIR/pi-gen/stage*/01-proxmark3/00-run-chroot.sh"
if ls $PM3_SCRIPT 2>/dev/null | head -1 | xargs -I {} bash -c '
script="$1"
if grep -q "cmake" "$script"; then
if grep -B50 "cmake" "$script" | grep -q "apt-get install.*cmake"; then
echo "PASS"
else
echo "FAIL"
fi
else
echo "SKIP"
fi
' _ {} | grep -q "PASS"; then
pass " PM3: cmake installed before use"
elif ls $PM3_SCRIPT 2>/dev/null | head -1 | xargs -I {} bash -c '
script="$1"
if grep -q "cmake" "$script"; then
if grep -B50 "cmake" "$script" | grep -q "apt-get install.*cmake"; then
echo "PASS"
else
echo "FAIL"
fi
else
echo "SKIP"
fi
' _ {} | grep -q "FAIL"; then
warn " PM3: cmake may not be installed before use"
fi
# Check dangerous-pi stage for pip3
DTPI_SCRIPT="$PROJECT_DIR/pi-gen/stage*/*/03-dangerous-pi/00-run-chroot.sh"
if ls $DTPI_SCRIPT 2>/dev/null | head -1 | xargs -I {} bash -c '
script="$1"
if grep -q "pip3 install" "$script"; then
if grep -B20 "pip3 install" "$script" | grep -qE "apt-get install.*python3-pip|command -v pip3"; then
echo "PASS"
else
echo "FAIL"
fi
else
echo "SKIP"
fi
' _ {} | grep -q "PASS"; then
pass " Dangerous-Pi: pip3 check before use"
elif ls $DTPI_SCRIPT 2>/dev/null | head -1 | xargs -I {} bash -c '
script="$1"
if grep -q "pip3 install" "$script"; then
if grep -B20 "pip3 install" "$script" | grep -qE "apt-get install.*python3-pip|command -v pip3"; then
echo "PASS"
else
echo "FAIL"
fi
else
echo "SKIP"
fi
' _ {} | grep -q "FAIL"; then
error " Dangerous-Pi: pip3 used without installation check"
fi
# Check PM3 dependencies by analyzing source
info "Analyzing PM3 source for required dependencies..."
PM3_SCRIPT_PATH="$PROJECT_DIR/pi-gen/stagePM3/01-proxmark3/00-run-chroot.sh"
if [ -f "$PM3_SCRIPT_PATH" ]; then
# Clone PM3 temporarily to analyze (lightweight, just for analysis)
PM3_TEMP="/tmp/pm3-preflight-check"
if [ ! -d "$PM3_TEMP" ]; then
info "Cloning Proxmark3 for dependency analysis..."
git clone --depth 1 --quiet https://github.com/RfidResearchGroup/proxmark3 "$PM3_TEMP" 2>/dev/null
fi
if [ -d "$PM3_TEMP" ]; then
# Header to package mapping (common Debian packages)
declare -A HEADER_TO_PKG=(
["lz4frame.h"]="liblz4-dev"
["lz4.h"]="liblz4-dev"
["readline/readline.h"]="libreadline-dev"
["readline.h"]="libreadline-dev"
["libusb.h"]="libusb-1.0-0-dev"
["libusb-1.0/libusb.h"]="libusb-1.0-0-dev"
["pthread.h"]="libc6-dev"
["zlib.h"]="zlib1g-dev"
["bzlib.h"]="libbz2-dev"
["jansson.h"]="libjansson-dev"
["bluez/bluetooth.h"]="libbluetooth-dev"
)
# Find all #include statements for system headers
FOUND_HEADERS=$(grep -rh "^#include <" "$PM3_TEMP/client" 2>/dev/null | \
grep -oE '<[^>]+>' | tr -d '<>' | sort -u)
# Check if required packages are installed
for header in $FOUND_HEADERS; do
pkg="${HEADER_TO_PKG[$header]}"
if [ -n "$pkg" ]; then
# Check if this package is in our install script
if grep -q "$pkg" "$PM3_SCRIPT_PATH"; then
pass " PM3: $pkg (for $header)"
else
error " PM3: Missing $pkg (required for #include <$header>)"
info " Add '$pkg' to apt-get install in stagePM3/01-proxmark3/00-run-chroot.sh"
fi
fi
done
# Also check for essential build tools
ESSENTIAL_DEPS=("cmake" "build-essential" "git" "pkg-config")
for dep in "${ESSENTIAL_DEPS[@]}"; do
if grep -q "$dep" "$PM3_SCRIPT_PATH"; then
pass " PM3: $dep (build tool)"
else
error " PM3: Missing $dep (essential build tool)"
info " Add '$dep' to apt-get install in stagePM3/01-proxmark3/00-run-chroot.sh"
fi
done
else
warn " Could not clone PM3 for analysis - using basic checks only"
# Fallback to basic check
for dep in cmake build-essential git; do
if grep -q "$dep" "$PM3_SCRIPT_PATH"; then
pass " PM3: $dep"
else
error " PM3: Missing $dep"
fi
done
fi
fi
# ============================================================================
section "6. DIRECTORY CREATION VALIDATION"
# ============================================================================
info "Checking for mkdir -p before file writes to non-standard dirs..."
WIFI_SCRIPT="$PROJECT_DIR/pi-gen/stage*/*/01-Wireless-AP/00-run-chroot.sh"
if [ -f "$(ls $WIFI_SCRIPT 2>/dev/null | head -1)" ]; then
# Check /etc/network/interfaces.d
if grep -q "cat.*>.*\/etc\/network\/interfaces.d" "$(ls $WIFI_SCRIPT 2>/dev/null | head -1)"; then
if grep -q "mkdir -p /etc/network/interfaces.d" "$(ls $WIFI_SCRIPT 2>/dev/null | head -1)"; then
pass " WiFi: /etc/network/interfaces.d created before use"
else
error " WiFi: /etc/network/interfaces.d not created (will fail)"
fi
fi
# Check lighttpd directories
if grep -q "cat.*>.*lighttpd" "$(ls $WIFI_SCRIPT 2>/dev/null | head -1)"; then
if grep -q "mkdir -p.*lighttpd.*conf-" "$(ls $WIFI_SCRIPT 2>/dev/null | head -1)"; then
pass " WiFi: lighttpd config directories created"
else
warn " WiFi: lighttpd directories may not exist"
fi
fi
fi
# ============================================================================
section "7. SOURCE FILE AVAILABILITY"
# ============================================================================
# Check if source files exist
for dir in app systemd scripts; do
if [ -d "$PROJECT_DIR/$dir" ]; then
pass "Source directory exists: $dir/"
else
error "Source directory missing: $dir/"
fi
done
if [ -f "$PROJECT_DIR/requirements.txt" ]; then
pass "requirements.txt exists"
else
error "requirements.txt missing"
fi
# ============================================================================
section "8. BUILD SCRIPT VALIDATION"
# ============================================================================
BUILD_SCRIPT="$PROJECT_DIR/build-image.sh"
if [ -f "$BUILD_SCRIPT" ]; then
pass "build-image.sh exists"
if [ -x "$BUILD_SCRIPT" ]; then
pass "build-image.sh is executable"
else
warn "build-image.sh is not executable"
fi
# Check if it uses our custom stages
if grep -q "stagePM3\|stageDangerousPi\|stage3\|stage4" "$BUILD_SCRIPT"; then
pass "build-image.sh references custom stages"
else
warn "build-image.sh may not reference custom stages"
fi
else
error "build-image.sh not found"
fi
# ============================================================================
section "9. PACKAGE NAME VALIDATION"
# ============================================================================
info "Validating package names against Debian repositories..."
# Create temporary container to query package database
TEMP_CONTAINER="preflight-pkg-check-$$"
docker run -d --name "$TEMP_CONTAINER" debian:trixie sleep 3600 > /dev/null 2>&1
docker exec "$TEMP_CONTAINER" apt-get update > /dev/null 2>&1
# Extract all package names from apt-get install commands
ALL_PACKAGES=$(grep -rh "apt-get install" "$PROJECT_DIR/pi-gen/stage"* 2>/dev/null | \
grep -v "^#" | \
sed 's/.*apt-get install//' | \
sed 's/-y//g; s/--no-install-recommends//g; s/--break-system-packages//g' | \
tr '\\' ' ' | \
tr '\n' ' ' | \
tr -s ' ' | \
grep -oE '[a-z0-9][a-z0-9+.-]+' | \
sort -u)
PACKAGE_ERRORS=0
for pkg in $ALL_PACKAGES; do
# Skip common flags, keywords, and commands
if echo "$pkg" | grep -qE '^(y|no|install|apt|get|update|upgrade|break|system|packages)$'; then
continue
fi
# Check if package exists in Debian repos
if docker exec "$TEMP_CONTAINER" apt-cache show "$pkg" > /dev/null 2>&1; then
pass " Package exists: $pkg"
else
error " Package NOT FOUND in Debian repos: $pkg"
info " Check for typos or hallucinated package names!"
((PACKAGE_ERRORS++))
fi
done
# Cleanup
docker rm -f "$TEMP_CONTAINER" > /dev/null 2>&1
if [ $PACKAGE_ERRORS -eq 0 ]; then
pass "All package names validated successfully"
else
error "Found $PACKAGE_ERRORS invalid package names"
info " Common mistakes: python-swig (should be: swig), libbluez-dev (should be: libbluetooth-dev)"
fi
# ============================================================================
section "10. FILE REFERENCE VALIDATION"
# ============================================================================
info "Checking for references to missing files..."
# Check for file copies in 00-run.sh scripts (outside chroot)
for script in "$PROJECT_DIR"/pi-gen/stage*/*/00-run.sh; do
if [ -f "$script" ]; then
STAGE_DIR=$(dirname "$script")
# Look for cp commands with source files
while read -r line; do
# Extract source path from cp commands
if echo "$line" | grep -q "^cp "; then
SRC=$(echo "$line" | awk '{print $2}' | sed 's/"//g')
# Skip if it's a flag, variable, or special path
if echo "$SRC" | grep -qE '^-|^\$|^\*|^/tmp|^/etc|^/var'; then
continue
fi
# Check if file exists relative to stage dir
if [ -f "$STAGE_DIR/$SRC" ] || [ -d "$STAGE_DIR/$SRC" ]; then
pass " Found: $SRC (in $(basename $(dirname "$script")))"
else
warn " Missing file reference: $SRC in $script"
fi
fi
done < <(grep -E "^cp " "$script" 2>/dev/null)
fi
done
# ============================================================================
section "11. SERVICE FILE SYNTAX VALIDATION"
# ============================================================================
info "Validating systemd service files..."
# Find all .service files in stage directories
for service_file in "$PROJECT_DIR"/pi-gen/stage*/*/*.service "$PROJECT_DIR"/systemd/*.service; do
if [ -f "$service_file" ]; then
BASENAME=$(basename "$service_file")
# Check for required sections
if grep -q "^\[Unit\]" "$service_file" && \
grep -q "^\[Service\]" "$service_file" && \
grep -q "^\[Install\]" "$service_file"; then
pass " Service file structure valid: $BASENAME"
else
error " Service file missing required sections: $BASENAME"
info " Required: [Unit], [Service], [Install]"
fi
# Check for ExecStart
if grep -q "^ExecStart=" "$service_file"; then
pass " Has ExecStart: $BASENAME"
else
error " Missing ExecStart in: $BASENAME"
fi
# Check for invalid __PLACEHOLDERS__ that weren't substituted
# Allow __DANGEROUS_PI_USER__ as it's intentionally substituted during build
UNSUBBED=$(grep "__.*__" "$service_file" 2>/dev/null | grep -v "__DANGEROUS_PI_USER__" || true)
if [ -n "$UNSUBBED" ]; then
warn " Found un-substituted placeholder in: $BASENAME"
echo "$UNSUBBED" | sed 's/^/ /'
fi
fi
done
# ============================================================================
section "12. ENVIRONMENT VARIABLE CHECKS"
# ============================================================================
info "Checking for undefined environment variables in scripts..."
for script in "$PROJECT_DIR"/pi-gen/stage*/*/[0-9][0-9]-*.sh; do
if [ -f "$script" ]; then
SCRIPT_NAME=$(basename "$script")
# Look for variable usage
USED_VARS=$(grep -oE '\$\{?[A-Z_][A-Z0-9_]*\}?' "$script" 2>/dev/null | \
sed 's/[${}]//g' | sort -u)
for var in $USED_VARS; do
# Check if variable is defined in script or is a known variable
if grep -q "^$var=" "$script" || \
grep -q "export $var=" "$script" || \
grep -q "$var=" "$script" || \
echo "$var" | grep -qE '^(ROOTFS_DIR|SCRIPT_DIR|DEFAULT_USER|DEFAULT_HOME|PATH|HOME|USER|HTTP|PM3_ROOT|BOOT_DIR|CONFIG_FILE|CMDLINE_FILE)$'; then
# Variable is defined, standard, or from external system (lighttpd config, PM3 build)
continue
else
warn " Potentially undefined variable: \$$var in $SCRIPT_NAME"
fi
done
fi
done
# ============================================================================
section "13. DISK SPACE CHECK"
# ============================================================================
info "Checking available disk space..."
REQUIRED_GB=15
AVAILABLE_KB=$(df /home/work/pi-gen-builder 2>/dev/null | tail -1 | awk '{print $4}')
AVAILABLE_GB=$((AVAILABLE_KB / 1024 / 1024))
if [ $AVAILABLE_GB -ge $REQUIRED_GB ]; then
pass "Sufficient disk space: ${AVAILABLE_GB}GB available (${REQUIRED_GB}GB required)"
else
error "Insufficient disk space: ${AVAILABLE_GB}GB available (${REQUIRED_GB}GB required)"
info " Pi-gen builds require ~15GB for image + cache"
info " Free up space or build will fail mid-download"
fi
# ============================================================================
section "14. NETWORK CONNECTIVITY CHECK"
# ============================================================================
info "Testing network connectivity to required repositories..."
# Test Debian repos
if curl -s --connect-timeout 5 http://deb.debian.org/debian/dists/trixie/Release > /dev/null 2>&1; then
pass "Can reach deb.debian.org"
else
error "Cannot reach deb.debian.org (Debian package repository)"
info " Check internet connection or firewall"
fi
# Test Raspberry Pi repos
if curl -s --connect-timeout 5 http://archive.raspberrypi.com/debian/dists/trixie/Release > /dev/null 2>&1; then
pass "Can reach archive.raspberrypi.com"
else
error "Cannot reach archive.raspberrypi.com (Raspberry Pi repository)"
fi
# Test GitHub (for PM3 clone)
if curl -s --connect-timeout 5 https://github.com > /dev/null 2>&1; then
pass "Can reach github.com"
else
error "Cannot reach github.com (needed for Proxmark3 clone)"
fi
# Test PM3 repo specifically
if git ls-remote https://github.com/RfidResearchGroup/proxmark3 > /dev/null 2>&1; then
pass "Proxmark3 repository is accessible"
else
error "Cannot access Proxmark3 repository"
info " URL: https://github.com/RfidResearchGroup/proxmark3"
fi
# ============================================================================
section "VALIDATION SUMMARY"
# ============================================================================
echo ""
echo "┌────────────────────────────────────────┐"
echo "│ VALIDATION RESULTS │"
echo "├────────────────────────────────────────┤"
printf "${GREEN}Checks Passed:${NC} %-4d │\n" $CHECKS_PASSED
printf "${YELLOW}Warnings:${NC} %-4d │\n" $WARNINGS
printf "${RED}Errors:${NC} %-4d │\n" $ERRORS
echo "└────────────────────────────────────────┘"
echo ""
if [ $ERRORS -gt 0 ]; then
echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${RED}║ BUILD WILL FAIL - FIX ERRORS BEFORE ATTEMPTING BUILD ║${NC}"
echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "Do NOT start a build until all errors are fixed."
echo "Each error listed above will cause build failure."
exit 1
elif [ $WARNINGS -gt 0 ]; then
echo -e "${YELLOW}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${YELLOW}║ WARNINGS DETECTED - REVIEW BEFORE BUILD ║${NC}"
echo -e "${YELLOW}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "Warnings may indicate potential issues but won't necessarily fail the build."
exit 0
else
echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ ALL CHECKS PASSED - READY TO BUILD ║${NC}"
echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "Build environment is validated. Safe to proceed with:"
echo " ./build-image.sh full"
exit 0
fi