- versions.env pins podman, conmon, crun, netavark, aardvark-dns, passt, and fuse-overlayfs to verified upstream source checksums; SlackBuild recipes, scripts/build-packages.sh, checksums.sh, release.sh, and update-versions.sh implement the reproducible pipeline; GitHub Actions workflows build in a Slackware container and publish releases without committing any binaries. - plugin/podman.plg installs/updates/removes all eight packages (the seven components plus the plugin's own unraid-podman scaffolding package) via upgradepkg, using the official Unraid array-event hook mechanism (event/disks_mounted, event/stopping) instead of editing /boot/config/go. rc.podman and the sbin/ helper scripts implement storage creation, config seeding/sync, preflight checks, autostart with per-container Safe-Mode, and package verify/update/rollback. - webui/plugins/podman implements the Dashboard, Containers, Pods, Images, Volumes, Networks, Logs, Terminal, Compose, and Settings panels against the approved mockup (webui/mockups/prototype.html), talking to podman system service exclusively via PodmanClient.php (libpod REST API over the Unix socket), with two documented exceptions: Terminal's one-shot exec model and Compose's use of the podman compose CLI, since libpod has no REST equivalent for either. - docs/ARCHITECTURE.md and docs/ROADMAP.md record the design decisions and honest current status (syntax-checked, unit- and integration-tested against fake sockets/servers; not yet run against a real Unraid/Podman/Slackware system). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
245 lines
9.1 KiB
Bash
Executable File
245 lines
9.1 KiB
Bash
Executable File
#!/bin/bash
|
|
# =============================================================================
|
|
# plugin/rc.d/rc.podman
|
|
#
|
|
# Init script for unraid-podman, in the classic Slackware BSD
|
|
# start|stop|restart|status style — Unraid has no systemd, so this script
|
|
# alone owns the entire process lifecycle (see docs/ARCHITECTURE.md
|
|
# section 6, "Start-/Stop-Skripte", and the top-level "Rahmenbedingungen"
|
|
# table on why that's the case).
|
|
#
|
|
# Staged by plugin/podman.plg to /etc/rc.d/rc.podman. Its boot-time
|
|
# invocation is registered (also by podman.plg) in /boot/config/go, gated on
|
|
# Unraid's "array started" event rather than running unconditionally at
|
|
# boot — the configured storage path (cache pool/disk) is not guaranteed to
|
|
# be available before that point. See ARCHITECTURE.md section 6.2.
|
|
#
|
|
# The individual responsibilities below are deliberately split into small,
|
|
# single-purpose scripts under /usr/local/sbin/ (staged from plugin/sbin/)
|
|
# rather than inlined here — see each script's own header comment for why
|
|
# it's separate:
|
|
# podman-preflight.sh startup validation
|
|
# podman-config.sh config seeding + sync
|
|
# podman-storage.sh podman.img create/mount/unmount
|
|
# podman-autostart.sh autostart chain
|
|
# podman-backup.sh config/package snapshots for rollback
|
|
# podman-verify-packages.sh package integrity checks
|
|
# podman-update-packages.sh package reconciliation/update
|
|
# podman-uninstall-cleanup.sh called from podman.plg's removepkg block
|
|
# =============================================================================
|
|
|
|
set -u
|
|
|
|
SBIN_DIR="/usr/local/sbin"
|
|
# shellcheck source=../sbin/podman-common.sh
|
|
. "$SBIN_DIR/podman-common.sh"
|
|
|
|
podman_load_cfg
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# podman_start
|
|
#
|
|
# See file header for the full sequence. Every step is expected to be
|
|
# idempotent (safe to re-run `rc.podman start` against an already-running
|
|
# instance without breaking anything) — preflight and podman-storage.sh
|
|
# both already implement this on their own, but we also short-circuit here
|
|
# if the service is clearly already up, to avoid doing redundant work.
|
|
# -----------------------------------------------------------------------------
|
|
podman_start() {
|
|
if [ "${PODMAN_ENABLED:-yes}" != "yes" ]; then
|
|
podman_log "start: PODMAN_ENABLED is not 'yes' in podman.cfg, not starting"
|
|
return 0
|
|
fi
|
|
|
|
if [ -S "$PODMAN_SOCKET" ] && [ -f "$PODMAN_SERVICE_PID_FILE" ] \
|
|
&& kill -0 "$(cat "$PODMAN_SERVICE_PID_FILE" 2> /dev/null)" 2> /dev/null; then
|
|
podman_log "start: already running (pid $(cat "$PODMAN_SERVICE_PID_FILE"))"
|
|
return 0
|
|
fi
|
|
|
|
podman_log "start: beginning startup sequence"
|
|
|
|
# 1. Preflight — aborts loudly (and already notified) on failure.
|
|
if ! "$SBIN_DIR/podman-preflight.sh"; then
|
|
podman_log_error "start: preflight checks failed, aborting"
|
|
return 1
|
|
fi
|
|
|
|
# 2. Ensure config exists (no-op if already seeded) and sync it to the
|
|
# RAM-root /etc/containers/ — see podman-config.sh.
|
|
"$SBIN_DIR/podman-config.sh" seed
|
|
if ! "$SBIN_DIR/podman-config.sh" sync; then
|
|
podman_log_error "start: config sync failed, aborting"
|
|
return 1
|
|
fi
|
|
|
|
# 3. Storage: create the image if this is a first start, then mount it.
|
|
if ! "$SBIN_DIR/podman-storage.sh" create; then
|
|
podman_log_error "start: storage creation failed, aborting"
|
|
return 1
|
|
fi
|
|
if ! "$SBIN_DIR/podman-storage.sh" mount; then
|
|
podman_log_error "start: storage mount failed, aborting"
|
|
return 1
|
|
fi
|
|
|
|
# 4. Restore persisted netavark network definitions (see
|
|
# docs/ARCHITECTURE.md section 8, Netzwerke) from the boot-persistent
|
|
# copy into the RAM-root config directory netavark reads from.
|
|
mkdir -p "$PODMAN_ETC_DIR/networks"
|
|
if [ -d "$PODMAN_NETWORKS_BOOT_DIR" ] && [ -n "$(ls -A "$PODMAN_NETWORKS_BOOT_DIR" 2> /dev/null)" ]; then
|
|
cp -a "$PODMAN_NETWORKS_BOOT_DIR"/. "$PODMAN_ETC_DIR/networks/"
|
|
podman_log "start: restored custom network definitions"
|
|
fi
|
|
|
|
# 5. Start the Podman API service, rootful, on a unix socket — see
|
|
# docs/ARCHITECTURE.md section 6.1 for why this runs as a persistent
|
|
# service rather than being started fresh per CLI/WebUI call:
|
|
# a shared process gives consistent state and event streaming.
|
|
# --time=0 disables the idle-shutdown timeout (this is a long-running
|
|
# daemon under our process management, not an on-demand activation).
|
|
mkdir -p "$PODMAN_RUN_DIR"
|
|
podman_log "start: starting podman system service on unix://$PODMAN_SOCKET"
|
|
nohup podman system service --time=0 "unix://$PODMAN_SOCKET" \
|
|
> "$PODMAN_LOG_DIR/podman-service.log" 2>&1 &
|
|
local service_pid=$!
|
|
echo "$service_pid" > "$PODMAN_SERVICE_PID_FILE"
|
|
|
|
# Wait for the socket to actually appear rather than assuming the fork
|
|
# succeeded instantly — up to 15s, polled every 200ms.
|
|
local waited=0
|
|
while [ ! -S "$PODMAN_SOCKET" ] && [ "$waited" -lt 15000 ]; do
|
|
sleep 0.2
|
|
waited=$((waited + 200))
|
|
if ! kill -0 "$service_pid" 2> /dev/null; then
|
|
podman_log_error "start: podman system service exited immediately — see $PODMAN_LOG_DIR/podman-service.log"
|
|
"$SBIN_DIR/podman-storage.sh" unmount || true
|
|
podman_notify "Podman failed to start" \
|
|
"podman system service exited immediately. See $PODMAN_LOG_DIR/podman-service.log." \
|
|
"alert"
|
|
return 1
|
|
fi
|
|
done
|
|
|
|
if [ ! -S "$PODMAN_SOCKET" ]; then
|
|
podman_log_error "start: timed out waiting for $PODMAN_SOCKET to appear"
|
|
return 1
|
|
fi
|
|
podman_log "start: podman system service is up (pid $service_pid)"
|
|
|
|
# 6. Autostart. A failure here is logged/notified by the script itself
|
|
# and does not abort rc.podman start — the service is already usable.
|
|
"$SBIN_DIR/podman-autostart.sh" || podman_log_error "start: autostart chain reported errors (see above)"
|
|
|
|
echo "running" > "$PODMAN_STATUS_FILE"
|
|
podman_log "start: startup sequence complete"
|
|
return 0
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# podman_stop
|
|
#
|
|
# Stops containers first (each with its own STOP_TIMEOUT grace period),
|
|
# then the API service, then unmounts storage — the reverse of start, so
|
|
# nothing is torn down out from under something still using it.
|
|
# -----------------------------------------------------------------------------
|
|
podman_stop() {
|
|
if [ ! -S "$PODMAN_SOCKET" ]; then
|
|
podman_log "stop: not running (no socket at $PODMAN_SOCKET)"
|
|
# Still attempt an unmount in case a prior stop was interrupted after
|
|
# the service went down but before the unmount completed.
|
|
"$SBIN_DIR/podman-storage.sh" unmount || true
|
|
rm -f "$PODMAN_STATUS_FILE"
|
|
return 0
|
|
fi
|
|
|
|
podman_log "stop: stopping running containers (timeout ${STOP_TIMEOUT:-10}s each)"
|
|
local running_ids
|
|
running_ids=$(podman --url "unix://$PODMAN_SOCKET" ps -q 2> /dev/null || true)
|
|
if [ -n "$running_ids" ]; then
|
|
local id
|
|
for id in $running_ids; do
|
|
podman --url "unix://$PODMAN_SOCKET" stop -t "${STOP_TIMEOUT:-10}" "$id" \
|
|
> /dev/null 2>&1 \
|
|
|| podman_log_error "stop: failed to stop container $id within timeout"
|
|
done
|
|
fi
|
|
|
|
podman_log "stop: stopping podman system service"
|
|
if [ -f "$PODMAN_SERVICE_PID_FILE" ]; then
|
|
local pid
|
|
pid=$(cat "$PODMAN_SERVICE_PID_FILE")
|
|
if kill -0 "$pid" 2> /dev/null; then
|
|
kill "$pid" 2> /dev/null || true
|
|
local waited=0
|
|
while kill -0 "$pid" 2> /dev/null && [ "$waited" -lt 10 ]; do
|
|
sleep 1
|
|
waited=$((waited + 1))
|
|
done
|
|
kill -0 "$pid" 2> /dev/null && kill -9 "$pid" 2> /dev/null || true
|
|
fi
|
|
rm -f "$PODMAN_SERVICE_PID_FILE"
|
|
fi
|
|
rm -f "$PODMAN_SOCKET"
|
|
|
|
"$SBIN_DIR/podman-storage.sh" unmount || podman_log_error "stop: storage unmount failed"
|
|
|
|
rm -f "$PODMAN_STATUS_FILE"
|
|
podman_log "stop: stopped"
|
|
return 0
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# podman_status
|
|
#
|
|
# Human-readable health summary — see docs/ARCHITECTURE.md section 16.2.
|
|
# -----------------------------------------------------------------------------
|
|
podman_status() {
|
|
echo "PODMAN_ENABLED: ${PODMAN_ENABLED:-yes}"
|
|
|
|
if [ -S "$PODMAN_SOCKET" ] && [ -f "$PODMAN_SERVICE_PID_FILE" ] \
|
|
&& kill -0 "$(cat "$PODMAN_SERVICE_PID_FILE" 2> /dev/null)" 2> /dev/null; then
|
|
echo "service: running (pid $(cat "$PODMAN_SERVICE_PID_FILE"), socket $PODMAN_SOCKET)"
|
|
else
|
|
echo "service: stopped"
|
|
fi
|
|
|
|
echo
|
|
"$SBIN_DIR/podman-storage.sh" status
|
|
|
|
echo
|
|
if [ -f "$PODMAN_AUTOSTART_FILE" ]; then
|
|
local count
|
|
count=$(grep -vcE '^\s*(#|$)' "$PODMAN_AUTOSTART_FILE" 2> /dev/null || echo 0)
|
|
echo "autostart entries: $count (see $PODMAN_AUTOSTART_FILE)"
|
|
fi
|
|
|
|
if [ -S "$PODMAN_SOCKET" ]; then
|
|
echo
|
|
echo "podman info:"
|
|
podman --url "unix://$PODMAN_SOCKET" info --format \
|
|
' containers: {{.Store.ContainerStore.Number}} images: {{.Store.ImageStore.Number}}' \
|
|
2> /dev/null || echo " (failed to query — service may still be initializing)"
|
|
fi
|
|
}
|
|
|
|
case "${1:-}" in
|
|
start)
|
|
podman_start
|
|
;;
|
|
stop)
|
|
podman_stop
|
|
;;
|
|
restart)
|
|
podman_stop
|
|
podman_start
|
|
;;
|
|
status)
|
|
podman_status
|
|
;;
|
|
*)
|
|
echo "usage: $0 {start|stop|restart|status}"
|
|
exit 1
|
|
;;
|
|
esac
|