#!/usr/bin/env bash # ============================================================================= # Leak Detection Sensor v2 (Raspberry Pi 4 Model B) install script # Target OS: Raspberry Pi OS Bookworm (64-bit, aarch64) # # Air-gapped support: # - If debs/ or wheelhouse/ is present, automatically switch to "offline mode". # - In offline mode, never access the network. If the bundle is only partial, # do not silently fall back to online; abort with a clear error (air-gap rule: # if the network is needed, always FLAG it). Pre-download is done on the build # machine via vendor.sh. # - Can be forced with the OFFLINE=1/0 environment variable. # - Log file owner: SERVICE_USER (default pi). # ============================================================================= set -euo pipefail cd "$(dirname "$0")" SERVICE_USER="${SERVICE_USER:-pi}" echo "==========================================" echo " Leak Detection Sensor v2 (Pi 4) install" echo "==========================================" # 0) Board model notice (not enforced) if [ -r /proc/device-tree/model ]; then MODEL="$(tr -d '\0' < /proc/device-tree/model)" echo "[INFO] Board: ${MODEL}" case "${MODEL}" in *"Raspberry Pi 4"*) : ;; *) echo "[WARN] This folder is intended for the Raspberry Pi 4 Model B only (continuing is possible)." ;; esac fi # Determine offline mode OFFLINE="${OFFLINE:-}" if [ -z "${OFFLINE}" ]; then if compgen -G "debs/*.deb" >/dev/null || [ -d wheelhouse ]; then OFFLINE=1 echo "[INFO] Offline bundle detected -> air-gapped offline install mode (no network)" else OFFLINE=0 echo "[INFO] No offline bundle -> online install mode (internet required)" fi fi # 1) System packages: RPi.GPIO replacement (lgpio) + requests + venv + camera app echo "[1/3] Installing system packages..." if [ "${OFFLINE}" = "1" ]; then if ! compgen -G "debs/*.deb" >/dev/null; then echo "[ERROR] Offline mode but debs/ is empty. Place the vendor.sh output (debs/) in this folder and re-run." >&2 exit 1 fi echo " Installing offline .deb (apt-get --no-download - automatic dependency ordering/resolution, no network)" # --no-install-recommends: vendor.sh does not include Recommends (--no-recommends), so match that. sudo apt-get install -y --no-install-recommends --no-download ./debs/*.deb else echo " Online apt install (internet required)" sudo apt-get update if apt-cache show rpicam-apps >/dev/null 2>&1; then CAM=rpicam-apps; else CAM=libcamera-apps; fi sudo apt-get install -y python3-rpi-lgpio python3-requests python3-venv "${CAM}" fi # 2) Python venv + requests echo "[2/3] Setting up Python virtual environment..." if [ ! -d venv ]; then python3 -m venv --system-site-packages venv echo " venv created" else echo " venv already exists - skipping" fi # shellcheck disable=SC1091 source venv/bin/activate if [ "${OFFLINE}" = "1" ]; then if [ ! -d wheelhouse ]; then echo "[ERROR] Offline mode but wheelhouse/ is missing. Place the vendor.sh output (wheelhouse/) and re-run." >&2 exit 1 fi echo " Installing offline wheels (--no-index - no network)" pip install --no-index --find-links=wheelhouse -r requirements.txt else echo " Online pip install (internet required)" pip install --upgrade pip pip install -r requirements.txt fi echo " Package installation complete" # 3) Log file (least privilege: owner write only - leak_sensor is the sole writer) echo "[3/3] Creating log file..." sudo mkdir -p /var/log sudo touch /var/log/leak_sensor.log if ! sudo chown "${SERVICE_USER}:${SERVICE_USER}" /var/log/leak_sensor.log; then echo "[WARN] chown failed - check whether SERVICE_USER=${SERVICE_USER} exists (must match the service user for file logging to work)" fi sudo chmod 640 /var/log/leak_sensor.log # 3.5) Lock config_local.py immediately (contains API_KEY - prevent world-readable until harden) # Hardening (harden_pi.sh) tightens this again later, but lock it to 600 right at install # time so it is not exposed even in the window between. Protected regardless of harden. if [ -f config_local.py ]; then chmod 600 config_local.py sudo chown "${SERVICE_USER}:${SERVICE_USER}" config_local.py 2>/dev/null || true echo " config_local.py permissions set to 600 (owner ${SERVICE_USER})" fi # 4) config_local notice (fail closed - if missing, execution aborts immediately) if [ ! -f config_local.py ]; then echo "" echo "[!] config_local.py is missing. Run the following, then fill in the real values:" echo " cp config_local.example.py config_local.py" fi echo "" echo "==========================================" echo " Installation complete! (OFFLINE=${OFFLINE})" echo "==========================================" echo " Run: source venv/bin/activate && python3 leak_sensor.py" echo " Service: see the systemd section in README.md" echo ""