#!/bin/sh # banger-first-boot — universal init wrapper for banger VMs. # # When passed as init= on the kernel cmdline (direct-boot images without # an initramfs), this script runs as PID 1. It: # 1. Mounts the essential virtual filesystems (/proc, /sys, /dev, /run) # 2. If systemd (or any init) is already installed, execs it immediately # 3. Otherwise: brings up the network, installs systemd + openssh-server # via the guest's native package manager, then execs systemd # # On subsequent boots (after systemd is installed), step 2 fires in <10ms. # # Test hooks: # RUN_PLAN=1 echo the install command instead of executing it # OS_RELEASE_FILE= override /etc/os-release for distro detection # BANGER_FIRST_BOOT_MARKER= override the marker file path set -eu log() { printf '[banger-first-boot] %s\n' "$*" >&2; } # --- Step 1: essential mounts (only when running as PID 1) --- if [ "$$" = "1" ]; then mount -t proc proc /proc 2>/dev/null || true mount -t sysfs sysfs /sys 2>/dev/null || true mount -t devtmpfs devtmpfs /dev 2>/dev/null || true mount -t tmpfs tmpfs /run 2>/dev/null || true mount -t tmpfs tmpfs /tmp 2>/dev/null || true fi # --- Step 2: if a real init exists, hand off immediately --- # (RUN_PLAN mode skips this so the dispatch logic can be tested on hosts # that have systemd installed.) if [ "${RUN_PLAN:-0}" != "1" ]; then for candidate_init in /usr/lib/systemd/systemd /lib/systemd/systemd /sbin/init; do if [ -x "$candidate_init" ]; then MARKER="${BANGER_FIRST_BOOT_MARKER:-/var/lib/banger/first-boot-pending}" if [ -f "$MARKER" ]; then rm -f "$MARKER" fi log "found init at $candidate_init; handing off" exec "$candidate_init" "$@" fi done fi # --- Step 3: no init found — we're on a container image, provision it --- log "no init system found; installing systemd + openssh-server" # Bring up network so apt-get/apk can reach package repos. # banger-network-bootstrap reads IP from /proc/cmdline (kernel ip= arg) # or /etc/banger-network.conf (written by vm_disk.patchRootOverlay). if [ -x /usr/local/libexec/banger-network-bootstrap ]; then log "bringing up network" /usr/local/libexec/banger-network-bootstrap || log "network bootstrap failed (continuing anyway)" fi # Detect distro DIST="" FAMILY="" OS_RELEASE_FILE="${OS_RELEASE_FILE:-/etc/os-release}" if [ -r "$OS_RELEASE_FILE" ]; then # shellcheck source=/dev/null . "$OS_RELEASE_FILE" DIST="${ID:-}" FAMILY="${ID_LIKE:-}" fi log "detected distro: ID=$DIST ID_LIKE=$FAMILY" # Dispatch install command CMD="" case "$DIST" in debian|ubuntu|kali|raspbian|linuxmint|pop) CMD="apt-get update && apt-get install -y systemd-sysv openssh-server" ;; alpine) CMD="apk add --no-cache openrc openssh systemd" ;; fedora|rhel|centos|rocky|almalinux) CMD="dnf install -y systemd openssh-server" ;; arch|archlinux|manjaro) CMD="pacman -Sy --noconfirm openssh" ;; opensuse*|suse) CMD="zypper --non-interactive install -y systemd openssh" ;; *) case " $FAMILY " in *" debian "*) CMD="apt-get update && apt-get install -y systemd-sysv openssh-server" ;; *" rhel "* | *" fedora "*) CMD="dnf install -y systemd openssh-server" ;; *" arch "*) CMD="pacman -Sy --noconfirm openssh" ;; *" suse "*) CMD="zypper --non-interactive install -y systemd openssh" ;; esac ;; esac if [ -z "$CMD" ]; then log "FATAL: no known install command for distro '$DIST' (ID_LIKE='$FAMILY')" log "drop to emergency shell" exec /bin/sh fi if [ "${RUN_PLAN:-0}" = "1" ]; then printf '%s\n' "$CMD" exit 0 fi log "running: $CMD" eval "$CMD" || { log "package install failed; dropping to shell" exec /bin/sh } # Remove first-boot marker MARKER="${BANGER_FIRST_BOOT_MARKER:-/var/lib/banger/first-boot-pending}" rm -f "$MARKER" # systemd should now be installed — find and exec it for candidate_init in /usr/lib/systemd/systemd /lib/systemd/systemd /sbin/init; do if [ -x "$candidate_init" ]; then log "provisioning complete; starting $candidate_init" # Unmount our temp mounts — systemd will re-mount them properly umount /tmp 2>/dev/null || true umount /run 2>/dev/null || true umount /dev 2>/dev/null || true umount /sys 2>/dev/null || true umount /proc 2>/dev/null || true exec "$candidate_init" "$@" fi done log "FATAL: init not found after install; dropping to shell" exec /bin/sh