#!/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