#!/bin/bash # ============================================================================= # plugin/sbin/podman-common.sh # # Shared constants and helper functions sourced by every other script under # plugin/sbin/ and by plugin/rc.d/rc.podman. Centralizing these here means: # - every script agrees on the same paths (no risk of one script writing # config to a path another script reads from a slightly different one), # - logging/notification behavior is consistent everywhere, # - each individual script stays focused on its one job instead of # re-implementing "how do I log this" or "where is podman.cfg". # # This file is NOT meant to be executed directly — it only defines # functions/variables for other scripts to source: # . "$(dirname "${BASH_SOURCE[0]}")/podman-common.sh" # # See docs/ARCHITECTURE.md sections 4 (Verzeichnislayout), 7 (Persistenz), # 15 (Logging), 16 (Fehlerbehandlung) for the design this implements. # ============================================================================= # We deliberately do NOT `set -e` in this file: it is sourced by scripts # that set their own error-handling mode, and a library changing its # caller's shell options would be a surprising action at a distance. # ----------------------------------------------------------------------------- # Path constants. # # PODMAN_BOOT_DIR is the persistent, source-of-truth configuration directory # on the Unraid flash device (survives reboots — see ARCHITECTURE.md 4.1). # Everything under /etc, /var, /usr is RAM-root and rebuilt from here (or # from the array/cache-backed storage dir) on every boot. # ----------------------------------------------------------------------------- PODMAN_BOOT_DIR="/boot/config/plugins/podman" PODMAN_CFG_FILE="$PODMAN_BOOT_DIR/podman.cfg" PODMAN_AUTOSTART_FILE="$PODMAN_BOOT_DIR/autostart" PODMAN_AUTOSTART_DELAY_FILE="$PODMAN_BOOT_DIR/autostart-delay" PODMAN_NETWORKS_BOOT_DIR="$PODMAN_BOOT_DIR/networks" PODMAN_BACKUP_DIR="$PODMAN_BOOT_DIR/backup" PODMAN_PLUGIN_LOG="$PODMAN_BOOT_DIR/plugin.log" # Runtime (RAM-root) locations rebuilt/synced on every rc.podman start. PODMAN_ETC_DIR="/etc/containers" PODMAN_RUN_DIR="/var/run/podman" PODMAN_SOCKET="$PODMAN_RUN_DIR/podman.sock" PODMAN_STATUS_FILE="$PODMAN_RUN_DIR/rc.podman.status" PODMAN_SERVICE_PID_FILE="$PODMAN_RUN_DIR/podman-service.pid" # ----------------------------------------------------------------------------- # podman_load_cfg # # Sources /boot/config/plugins/podman/podman.cfg (the user-editable settings # file, see config/podman.cfg.example) and applies safe defaults for any # setting that file doesn't define — so every other script can simply # reference $STORAGE_PATH, $PODMAN_ENABLED, etc. after calling this, without # each script having its own copy of the defaults. # ----------------------------------------------------------------------------- podman_load_cfg() { # Defaults, applied BEFORE sourcing podman.cfg so the file only needs to # override what the user actually wants to change. STORAGE_PATH="/mnt/cache/system/podman" STORAGE_IMAGE_SIZE_GB="20" PODMAN_ENABLED="yes" STOP_TIMEOUT="10" CONFIG_SCHEMA_VERSION="1" if [ -f "$PODMAN_CFG_FILE" ]; then # shellcheck source=/dev/null . "$PODMAN_CFG_FILE" fi # Values derived from STORAGE_PATH, computed after sourcing so a custom # STORAGE_PATH is honored. PODMAN_STORAGE_IMAGE="$STORAGE_PATH/podman.img" PODMAN_LOG_DIR="$STORAGE_PATH/logs" PODMAN_GRAPHROOT="/var/lib/containers/storage" } # ----------------------------------------------------------------------------- # podman_log # # Writes a timestamped line to both stdout (so it shows up in `rc.podman` # invocations and CI/manual runs) and to the persistent plugin log on flash # (so it survives a reboot — see ARCHITECTURE.md section 15, Logging). Kept # deliberately terse (no log levels/rotation logic here) — this is for # install/lifecycle events, not container output, which lives under # $PODMAN_LOG_DIR instead. # ----------------------------------------------------------------------------- podman_log() { local msg="$1" local line line="$(date -u +'%Y-%m-%dT%H:%M:%SZ') [podman] $msg" echo "$line" # Best-effort: the boot directory should always exist post-install, but # never let logging itself fail a caller that has `set -e`. mkdir -p "$PODMAN_BOOT_DIR" 2> /dev/null || true echo "$line" >> "$PODMAN_PLUGIN_LOG" 2> /dev/null || true } # ----------------------------------------------------------------------------- # podman_log_error # # Like podman_log, but also mirrors the message to the system log via # `logger`, so it is visible in Unraid's Tools -> System Log GUI without the # user having to know where the plugin's own log lives. Reserved for # conditions the user should actually notice (see ARCHITECTURE.md 16.2). # ----------------------------------------------------------------------------- podman_log_error() { local msg="$1" podman_log "ERROR: $msg" if command -v logger > /dev/null 2>&1; then logger -t podman-plugin "$msg" fi } # ----------------------------------------------------------------------------- # podman_notify [importance] # # Surfaces a message via Unraid's own GUI notification system # (/usr/local/emhttp/webGui/scripts/notify) instead of inventing a parallel # notification mechanism — see ARCHITECTURE.md section 16.2. Silently # no-ops if that script isn't present (e.g. when running outside a real # Unraid system, such as in CI/lint contexts). # # importance: "normal" (default), "warning", or "alert". # ----------------------------------------------------------------------------- podman_notify() { local subject="$1" local description="$2" local importance="${3:-normal}" local notify_bin="/usr/local/emhttp/webGui/scripts/notify" podman_log "notify [$importance] $subject: $description" if [ -x "$notify_bin" ]; then "$notify_bin" -e "unraid-podman" -s "$subject" -d "$description" -i "$importance" \ > /dev/null 2>&1 || true fi } # ----------------------------------------------------------------------------- # podman_storage_path_is_safe # # Refuses storage paths under /mnt/user (FUSE/shfs) — see # ARCHITECTURE.md section 4.3 for why the overlay storage driver must live # on a real mounted filesystem (cache pool or a specific disk), not shfs. # Returns 0 (safe) or 1 (unsafe) and prints a reason on failure. # ----------------------------------------------------------------------------- podman_storage_path_is_safe() { local path="$1" case "$path" in /mnt/user/*|/mnt/user) echo "STORAGE_PATH ($path) is under /mnt/user (FUSE/shfs)." >&2 echo "The overlay storage driver needs a real mounted filesystem —" >&2 echo "use a cache pool or a specific disk path instead, e.g. /mnt/cache/system/podman." >&2 return 1 ;; esac return 0 } # ----------------------------------------------------------------------------- # podman_require_command # # Fails loudly (rather than letting a script continue and fail confusingly # three steps later) if a required binary isn't on PATH. # ----------------------------------------------------------------------------- podman_require_command() { local bin="$1" if ! command -v "$bin" > /dev/null 2>&1; then podman_log_error "required command not found: $bin" return 1 fi }