Files
unraid-podman/plugin/sbin/podman-common.sh
T
maggesandClaude Sonnet 5 2e6832903b
Build Packages / Build .txz packages (push) Failing after 4s
Lint / ShellCheck (push) Successful in 11s
Lint / Validate .plg XML (push) Successful in 12s
Lint / EditorConfig (push) Successful in 5s
Fix CI: silence false-positive ShellCheck/EditorConfig findings
- podman-common.sh: file-wide shellcheck disable=SC2034 for its
  constants, which are intentionally defined for other scripts to
  source, not used within the file itself (ShellCheck can't see
  cross-file usage in that direction). Confirmed as the actual cause
  of the failing ShellCheck job via the real Gitea Actions log.
- podman-storage.sh: use the retry counter in cmd_unmount's log
  message instead of leaving it genuinely unused (real SC2034 hit,
  not a false positive).
- lint.yml: replace the editorconfig-checker/action-* marketplace
  action with a pinned, checksum-verified direct binary download —
  more robust than relying on third-party action resolution on
  self-hosted Gitea Actions runners — and disable its IndentSize
  check, which false-positives on Markdown's nested-list indentation
  and shell heredoc bodies. scripts/dev/lint.sh mirrors the same check
  for local use. All three lint.yml jobs verified passing locally.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 11:18:25 +00:00

184 lines
7.8 KiB
Bash
Executable File

#!/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.
# shellcheck disable=SC2034
# This whole file is a "library" of constants and functions for OTHER
# scripts to `source` — see the header above. ShellCheck analyzes each file
# in isolation and has no way to see that e.g. $PODMAN_AUTOSTART_FILE is
# used in podman-autostart.sh, not here, so it flags every constant below
# as "appears unused". That is a false positive for this file's actual
# role; disabling SC2034 file-wide (this directive, placed before any code,
# applies to the whole file per ShellCheck's own rules) is more honest than
# scattering 15+ identical per-line suppressions.
# -----------------------------------------------------------------------------
# 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 <message>
#
# 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 <message>
#
# 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 <subject> <description> [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 <binary>
#
# 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
}