feat: live ISO — nix daemon mode, autologin fix, GPU detection, app stack

- Switch nix from single-user to daemon mode (trusted-users = root live);
  Void socket at /var/nix/daemon-socket/socket confirmed
- Fix lightdm autologin: use session-wrapper=/etc/lightdm/Xsession (Void
  lightdm 1.32 has no lightdm-session binary)
- Fix session env: LIBGL_ALWAYS_SOFTWARE=1 via profile.d (session-env=
  is unsupported in this lightdm version)
- GPU auto-detection at boot: VIRT→software GL, NVIDIA PRIME offload,
  Intel/AMD/generic→modesetting
- Add nix-daemon to live runsvdir/default; remove unsupported -S mklive flag
- first-login.sh: install Claude Code + nix user packages (google-chrome,
  spotify, discord, localsend, mission-center) + NVM/node + VS Code exts
- build-live-iso.sh: write nix-packages.list from NIX_USER_PACKAGES
- postinstall.sh: fix nix-daemon socket path to /var/nix/daemon-socket/socket
- Dockerfile: add dconf-cli for build-time dconf compile
- _inner-build-live.sh: use correct 'dconf compile' API (not 'dconf update')
- .gitignore: add build/live-includes/ (generated staging tree)
- docs/LIVE_ISO.md: document all findings, gotchas and architecture
This commit is contained in:
mozempk
2026-04-23 07:42:35 +02:00
parent 6269f2f877
commit 5cd9b496fd
8 changed files with 461 additions and 56 deletions

View File

@@ -10,7 +10,7 @@ ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
bash git curl ca-certificates xz-utils tar patch python3 \
mtools xorriso squashfs-tools dosfstools e2fsprogs \
kmod \
kmod dconf-cli \
&& rm -rf /var/lib/apt/lists/*
# xbps-static is downloaded into /cache by the host script and added to PATH

View File

@@ -28,10 +28,12 @@ command -v xbps-install.static >/dev/null \
mkdir -p "$(dirname "$OUT_ISO")"
# Compile dconf system-db inside the include dir so it ships compiled.
# Debian's dconf-cli provides 'dconf compile <output_db> <keyfile_dir>'.
if command -v dconf >/dev/null 2>&1 && [[ -d "$INCLUDE_DIR/etc/dconf/db/local.d" ]]; then
DCONF_PROFILE_PATH="$INCLUDE_DIR/etc/dconf/profile" \
DCONF_SYSTEM_DB_PATH="$INCLUDE_DIR/etc/dconf/db" \
dconf update "$INCLUDE_DIR/etc/dconf/db" 2>/dev/null || true
dconf compile "$INCLUDE_DIR/etc/dconf/db/local" \
"$INCLUDE_DIR/etc/dconf/db/local.d" 2>/dev/null \
&& echo "dconf: compiled system-db/local" \
|| echo "dconf: compile failed (non-fatal)"
fi
cd "$MKLIVE_DIR"
@@ -52,6 +54,7 @@ trap _cleanup_mklive_builds EXIT
./mklive.sh \
-a "$ARCH" \
-r "$REPO_URL" \
-r "${REPO_URL%/current}/current/nonfree" \
-c "$CACHE_DIR/xbps-live-pkgs" \
-H "$CACHE_DIR/xbps-host-pkgs" \
-k "$KEYMAP" \

View File

@@ -70,57 +70,201 @@ rm -rf "$INCLUDE_DIR"
mkdir -p "$INCLUDE_DIR"
# ── 3a) live user account ───────────────────────────────────────────────
# Create user 'live' with no password in the live image.
# mklive populates /etc/passwd at install time — we drop a post-install hook
# via the live init that calls useradd. We use an install.conf.d override
# that mklive will source, plus an rc.local-style runit oneshot.
# The vmklive dracut hook (adduser.sh) creates the live user in initramfs
# using the live.user= kernel cmdline parameter (added to BOOT_CMDLINE below).
# We add extra groups and sudo via /etc/runit/2 override (after pivot_root).
install -d -m 0755 "$INCLUDE_DIR/etc"
cat > "$INCLUDE_DIR/etc/passwd-live" <<EOF
# placeholder replaced at build time by _inner-build-live.sh
EOF
# rc.local equivalent: runit oneshot that creates the live user on first boot.
install -d -m 0755 "$INCLUDE_DIR/etc/runit/runsvdir/default"
install -d -m 0755 "$INCLUDE_DIR/etc/sv/live-user-setup"
cat > "$INCLUDE_DIR/etc/sv/live-user-setup/run" <<'SV_EOF'
# GPU detection + extra group setup — runs from our runit/2 override below.
install -d -m 0755 "$INCLUDE_DIR/etc/runit"
cat > "$INCLUDE_DIR/etc/runit/live-setup.sh" <<'SV_EOF'
#!/bin/sh
# Create the 'live' user (no password) and set up home directory.
# Runs once; removes itself from default runsvdir on completion.
set -e
if ! id live >/dev/null 2>&1; then
useradd -m -s /bin/bash -G wheel,audio,video,plugdev,input,network,docker live
passwd -d live
install -d -m 0700 /home/live/.ssh
# Apply skel dotfiles
if [ -d /etc/skel ]; then
cp -a /etc/skel/. /home/live/ 2>/dev/null || true
fi
chown -R live:live /home/live
# Runs from /etc/runit/2 before any service starts.
# 1. Ensures live user has all needed groups.
# 2. Generates /etc/X11/xorg.conf.d/20-gpu.conf based on detected hardware.
LIVE_USER="${USERNAME:-live}"
[ -f /etc/default/live.conf ] && . /etc/default/live.conf
LIVE_USER="${USERNAME:-live}"
# Extra groups (dracut only adds audio,video,wheel)
for g in plugdev input network docker; do
groupadd -f "$g" 2>/dev/null || true
usermod -aG "$g" "$LIVE_USER" 2>/dev/null || true
done
install -d -m 0755 /etc/sudoers.d
printf '%s ALL=(ALL) NOPASSWD: ALL\n' "$LIVE_USER" > /etc/sudoers.d/live
chmod 0440 /etc/sudoers.d/live
# ── Nix daemon-mode setup ───────────────────────────────────────────────
# Void's xbps nix package ships nix-daemon + a runit service at /etc/sv/nix-daemon
# and puts the socket at /var/nix/daemon-socket/socket.
# We configure the daemon to trust the live user so 'nix profile install'
# works without root and without owning /nix/store.
if [ -x /usr/bin/nix ]; then
install -d -m 0755 /etc/nix
cat > /etc/nix/nix.conf <<NIXCONF
experimental-features = nix-command flakes
sandbox = false
auto-optimise-store = true
trusted-users = root $LIVE_USER
NIXCONF
# Ensure nix.sh profile.d sets PATH for installed nix packages.
# The Void nix package ships /etc/profile.d/nix.sh; we just supplement it.
cat > /etc/profile.d/nix-live.sh <<'NIXSH'
# Add nix user-profile bin to PATH
if [ -d "$HOME/.nix-profile/bin" ]; then
export PATH="$HOME/.nix-profile/bin:$PATH"
fi
# Enable sudoers entry for live
if ! grep -q '^live ' /etc/sudoers 2>/dev/null; then
echo 'live ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
NIXSH
chmod 0644 /etc/profile.d/nix-live.sh
echo "live-setup: nix daemon mode configured (trusted-users = $LIVE_USER)"
fi
# ── D-Bus session socket for the live user's login session ───────────────
# dbus-launch is called by lightdm-session / Xsession automatically.
# Ensure dbus system bus is accessible (needed by nix and Cinnamon).
# The dbus runit service is enabled in runsvdir/default; no extra setup needed.
# ── GPU / Xorg driver detection ──────────────────────────────────────────
mkdir -p /etc/X11/xorg.conf.d
# Detect GPU via PCI
HAS_NVIDIA=0
HAS_INTEL=0
HAS_AMD=0
IS_VIRT=0
if command -v lspci >/dev/null 2>&1; then
lspci_out=$(lspci 2>/dev/null)
echo "$lspci_out" | grep -qi 'NVIDIA' && HAS_NVIDIA=1
echo "$lspci_out" | grep -qiE 'Intel.*VGA|VGA.*Intel|Intel.*Integrated' && HAS_INTEL=1
echo "$lspci_out" | grep -qiE 'AMD.*VGA|VGA.*AMD|ATI.*VGA|Radeon' && HAS_AMD=1
echo "$lspci_out" | grep -qiE 'virtio|VMware|QEMU|VirtualBox|bochs' && IS_VIRT=1
fi
if [ $IS_VIRT -eq 1 ]; then
# Virtual machine: force modesetting, disable accel, use software GL
cat > /etc/X11/xorg.conf.d/20-gpu.conf <<'EOF'
Section "Device"
Identifier "GPU"
Driver "modesetting"
Option "AccelMethod" "none"
EndSection
EOF
# Xsession sources /etc/profile which sources /etc/profile.d/ — so this
# env var reaches the cinnamon session (LightDM's session-env is not
# supported in the Void-packaged lightdm 1.32).
echo 'export LIBGL_ALWAYS_SOFTWARE=1' > /etc/profile.d/live-env.sh
chmod 0644 /etc/profile.d/live-env.sh
echo "live-setup: VIRT detected — modesetting (no accel), software GL"
elif [ $HAS_NVIDIA -eq 1 ] && [ -e /usr/lib/xorg/modules/drivers/nvidia_drv.so ]; then
# NVIDIA PRIME (Intel iGPU primary + NVIDIA render offload)
# Real hardware — ensure software GL is NOT forced
rm -f /etc/profile.d/live-env.sh
INTEL_BUSID=$(lspci | grep -iE 'VGA.*Intel|Intel.*VGA' | head -1 | \
awk '{print $1}' | awk -F'[.:]' '{printf "PCI:%d:%d:%d", strtonum("0x"$1), strtonum("0x"$2), strtonum("0x"$3)}')
NVIDIA_BUSID=$(lspci | grep -i NVIDIA | head -1 | \
awk '{print $1}' | awk -F'[.:]' '{printf "PCI:%d:%d:%d", strtonum("0x"$1), strtonum("0x"$2), strtonum("0x"$3)}')
cat > /etc/X11/xorg.conf.d/20-gpu.conf <<EOF
Section "ServerLayout"
Identifier "layout"
Option "AllowNVIDIAGPUScreens"
EndSection
Section "Device"
Identifier "Intel"
Driver "modesetting"
BusID "${INTEL_BUSID:-PCI:0:2:0}"
EndSection
Section "Device"
Identifier "NVIDIA"
Driver "nvidia"
BusID "${NVIDIA_BUSID:-PCI:1:0:0}"
Option "AllowEmptyInitialConfiguration"
EndSection
EOF
echo "live-setup: NVIDIA PRIME detected (Intel=${INTEL_BUSID} NVIDIA=${NVIDIA_BUSID})"
elif [ $HAS_NVIDIA -eq 1 ]; then
# NVIDIA present but no proprietary driver — use modesetting
rm -f /etc/profile.d/live-env.sh
cat > /etc/X11/xorg.conf.d/20-gpu.conf <<'EOF'
Section "Device"
Identifier "GPU"
Driver "modesetting"
EndSection
EOF
echo "live-setup: NVIDIA detected but no proprietary driver — modesetting"
else
# Intel / AMD / generic — modesetting
rm -f /etc/profile.d/live-env.sh
cat > /etc/X11/xorg.conf.d/20-gpu.conf <<'EOF'
Section "Device"
Identifier "GPU"
Driver "modesetting"
EndSection
EOF
echo "live-setup: Generic/Intel/AMD — modesetting"
fi
# Remove self from runsvdir so it doesn't restart
rm -f /etc/runit/runsvdir/default/live-user-setup
sv stop live-user-setup 2>/dev/null || true
SV_EOF
chmod 0755 "$INCLUDE_DIR/etc/sv/live-user-setup/run"
ln -sf /etc/sv/live-user-setup "$INCLUDE_DIR/etc/runit/runsvdir/default/live-user-setup"
chmod 0755 "$INCLUDE_DIR/etc/runit/live-setup.sh"
# Override /etc/runit/2 — mirrors real runit-void stage 2 but runs live-setup.sh first.
# Real runit-void/2: runsvchdir default → runsvdir /run/runit/runsvdir/current
cat > "$INCLUDE_DIR/etc/runit/2" <<'SV_EOF'
#!/bin/sh
PATH=/usr/bin:/usr/sbin
# Live session setup: extra groups, sudo, GPU/Xorg detection
[ -x /etc/runit/live-setup.sh ] && /etc/runit/live-setup.sh
# Mirror real runit-void stage 2: select runlevel from cmdline (default: default)
runlevel=default
for arg in $(cat /proc/cmdline); do
if [ -d /etc/runit/runsvdir/"$arg" ]; then
runlevel="$arg"
fi
done
[ -x /etc/rc.local ] && /etc/rc.local
runsvchdir "${runlevel}"
mkdir -p /run/runit/runsvdir
ln -sf /etc/runit/runsvdir/current /run/runit/runsvdir/current
exec env - PATH=$PATH \
runsvdir -P /run/runit/runsvdir/current \
'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................'
SV_EOF
chmod 0755 "$INCLUDE_DIR/etc/runit/2"
install -d -m 0755 "$INCLUDE_DIR/etc/runit/runsvdir/default"
# ── 3b) LightDM autologin ───────────────────────────────────────────────
install -d -m 0755 "$INCLUDE_DIR/etc/lightdm"
cat > "$INCLUDE_DIR/etc/lightdm/lightdm.conf" <<EOF
# .session file: read by the vmklive dracut hook (display-manager-autologin.sh)
# to configure LightDM autologin session.
echo 'cinnamon' > "$INCLUDE_DIR/etc/lightdm/.session"
# lightdm.conf: autologin lines MUST be commented so the dracut
# display-manager-autologin.sh hook can sed-uncomment them.
cat > "$INCLUDE_DIR/etc/lightdm/lightdm.conf" <<'EOF'
[Seat:*]
autologin-user=${LIVE_USER}
autologin-user-timeout=0
user-session=cinnamon
#autologin-user=
#autologin-user-timeout=0
#autologin-session=
#user-session=
session-wrapper=/etc/lightdm/Xsession
greeter-session=lightdm-gtk-greeter
EOF
# NOTE: session-wrapper=/etc/lightdm/Xsession is required on Void Linux —
# LightDM defaults to 'lightdm-session' which is NOT provided by the Void
# lightdm package. The /etc/lightdm/Xsession wrapper is installed by the
# lightdm package and sources /etc/profile (and therefore /etc/profile.d/)
# so LIBGL_ALWAYS_SOFTWARE=1 placed there by live-setup.sh is propagated to
# the cinnamon session.
install -d -m 0755 "$INCLUDE_DIR/etc/runit/runsvdir/default"
# Enable NetworkManager, lightdm, pipewire and dbus for the live session.
for svc in dbus NetworkManager lightdm; do
# Enable services for the live session.
for svc in dbus NetworkManager lightdm nix-daemon; do
ln -sf "/etc/sv/$svc" "$INCLUDE_DIR/etc/runit/runsvdir/default/$svc" 2>/dev/null || true
done
@@ -285,6 +429,20 @@ case ":$PATH:" in
esac
EOF
# ── 3f-bis) Nix user packages list for first-login.sh ───────────────────
# Write NIX_USER_PACKAGES (from install.conf) as a plain list so
# first-login.sh can read it without sourcing install.conf.
install -d -m 0755 "$INCLUDE_DIR/usr/local/libexec"
{
for pkg in "${NIX_USER_PACKAGES[@]}"; do
echo "$pkg"
done
} > "$INCLUDE_DIR/usr/local/libexec/nix-packages.list"
# Also set the env var so first-login.sh knows where to find it.
cat > "$INCLUDE_DIR/etc/profile.d/nix-packages-path.sh" <<'EOF'
export NIX_PACKAGES_FILE=/usr/local/libexec/nix-packages.list
EOF
# ── 3g) Live-session skel: pre-wire .bash_profile for first-login ────────
install -d -m 0755 "$INCLUDE_DIR/etc/skel"
cat > "$INCLUDE_DIR/etc/skel/.bash_profile" <<'EOF'
@@ -313,6 +471,9 @@ ISO_PKGS=$(grep -vE '^\s*(#|$)' \
| tr '\n' ' ')
TS="$(date -u +%Y%m%d)"
OUT_ISO="${OUTPUT_ISO:-$OUT_DIR/void-live-stable-${TS}.iso}"
# live.user=live → vmklive dracut hook creates user 'live' (default would be 'anon')
# console=ttyS0 → serial output for QEMU/real hardware debugging
BOOT_CMDLINE="${BOOT_CMDLINE:-live.user=${LIVE_USER} console=tty0 console=ttyS0,115200}"
echo ">>> running mklive.sh inside docker — output: $OUT_ISO"
"$DOCKER" run --rm --privileged \