#!/bin/bash # ============================================================================= # plugin/sbin/podman-preflight.sh # # Startup validation, run by `rc.podman start` BEFORE anything is mounted or # started. The goal is to fail fast with one clear message, instead of # letting `podman system service` fail three steps later with a cryptic # error. See docs/ARCHITECTURE.md section 16.1 (Startup-Robustheit). # # Every check below is independent and all of them run even if an earlier # one fails, so a single invocation reports every problem at once rather # than forcing the user through a fix-one-rerun-find-the-next loop. # # Exit code: 0 if every check passed, 1 if any failed (with all failures # already logged/notified by that point). # ============================================================================= set -u # deliberately not -e: see the "run every check" note above SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=./podman-common.sh . "$SCRIPT_DIR/podman-common.sh" podman_load_cfg FAILURES=0 fail() { podman_log_error "preflight: $1" FAILURES=$((FAILURES + 1)) } ok() { podman_log "preflight: OK - $1" } # ----------------------------------------------------------------------------- # 1. Required binaries are actually installed. # ----------------------------------------------------------------------------- for bin in podman conmon crun mount umount xfs_repair mkfs.xfs; do if command -v "$bin" > /dev/null 2>&1; then ok "$bin found" else fail "required binary '$bin' not found on PATH — is the plugin fully installed?" fi done for helper in /usr/libexec/podman/netavark /usr/libexec/podman/aardvark-dns; do if [ -x "$helper" ]; then ok "$helper found" else fail "required helper '$helper' not found — is the plugin fully installed?" fi done # ----------------------------------------------------------------------------- # 2. Storage path configured, safe, and mounted (cache pool / disk, not # /mnt/user — see docs/ARCHITECTURE.md section 4.3). # ----------------------------------------------------------------------------- if podman_storage_path_is_safe "$STORAGE_PATH" 2> /tmp/podman-preflight-storage-safety.log; then ok "STORAGE_PATH ($STORAGE_PATH) is not under /mnt/user" else fail "$(cat /tmp/podman-preflight-storage-safety.log)" fi rm -f /tmp/podman-preflight-storage-safety.log if [ -d "$STORAGE_PATH" ] && mountpoint -q "$STORAGE_PATH" 2> /dev/null; then ok "STORAGE_PATH ($STORAGE_PATH) is a mounted filesystem" elif [ -d "$STORAGE_PATH" ]; then # Not every valid target is a mountpoint itself (e.g. a subdirectory of a # cache pool root) — warn rather than fail, but only if the parent chain # is mounted somewhere. if findmnt -T "$STORAGE_PATH" > /dev/null 2>&1; then ok "STORAGE_PATH ($STORAGE_PATH) resolves onto a mounted filesystem" else fail "STORAGE_PATH ($STORAGE_PATH) does not appear to be on a mounted filesystem" fi else fail "STORAGE_PATH ($STORAGE_PATH) does not exist — is the configured cache pool/disk present and started?" fi # ----------------------------------------------------------------------------- # 3. Free space check (only meaningful once STORAGE_PATH exists). # ----------------------------------------------------------------------------- if [ -d "$STORAGE_PATH" ]; then available_kb=$(df --output=avail -k "$STORAGE_PATH" 2> /dev/null | tail -n1 | tr -d '[:space:]') if [ -n "${available_kb:-}" ]; then available_gb=$((available_kb / 1024 / 1024)) if [ -f "$PODMAN_STORAGE_IMAGE" ]; then # Image already exists — just warn if the pool itself is nearly full, # since podman.img growth or new image pulls need headroom too. if [ "$available_gb" -lt 2 ]; then fail "less than 2G free on $STORAGE_PATH (${available_gb}G) — image pulls will likely fail" else ok "${available_gb}G free on $STORAGE_PATH" fi elif [ "$available_gb" -lt "$STORAGE_IMAGE_SIZE_GB" ]; then fail "not enough free space on $STORAGE_PATH to create a ${STORAGE_IMAGE_SIZE_GB}G podman.img (only ${available_gb}G free)" else ok "${available_gb}G free on $STORAGE_PATH (enough for a fresh ${STORAGE_IMAGE_SIZE_GB}G podman.img)" fi fi fi # ----------------------------------------------------------------------------- # 4. Kernel supports cgroup v2 (required by crun/netavark's expectations). # ----------------------------------------------------------------------------- if [ -f /sys/fs/cgroup/cgroup.controllers ]; then ok "cgroup v2 unified hierarchy is active" else fail "cgroup v2 unified hierarchy not detected (/sys/fs/cgroup/cgroup.controllers missing) — check Unraid's syslinux cgroup boot parameters" fi # ----------------------------------------------------------------------------- # 5. No orphaned socket/pidfile from a previous unclean shutdown. # ----------------------------------------------------------------------------- if [ -S "$PODMAN_SOCKET" ]; then if [ -f "$PODMAN_SERVICE_PID_FILE" ] && kill -0 "$(cat "$PODMAN_SERVICE_PID_FILE")" 2> /dev/null; then fail "podman system service already appears to be running (pid $(cat "$PODMAN_SERVICE_PID_FILE")) — is rc.podman already started?" else podman_log "preflight: removing orphaned socket $PODMAN_SOCKET from a previous unclean shutdown" rm -f "$PODMAN_SOCKET" ok "cleared orphaned socket" fi else ok "no orphaned podman.sock" fi # ----------------------------------------------------------------------------- # 6. Boot-config directory exists (i.e. 'seed' has run at least once). # ----------------------------------------------------------------------------- if [ -f "$PODMAN_CFG_FILE" ]; then ok "$PODMAN_CFG_FILE present" else fail "$PODMAN_CFG_FILE missing — run 'podman-config.sh seed' (should happen automatically on install)" fi # ----------------------------------------------------------------------------- # Summary # ----------------------------------------------------------------------------- if [ "$FAILURES" -gt 0 ]; then podman_notify "Podman preflight checks failed" \ "$FAILURES check(s) failed — podman was not started. See $PODMAN_PLUGIN_LOG for details." \ "alert" podman_log_error "preflight: $FAILURES check(s) failed, aborting start" exit 1 fi podman_log "preflight: all checks passed" exit 0