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

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@ authorized_keys
# ── Generated build staging (build-iso.sh populates this at build time) ─ # ── Generated build staging (build-iso.sh populates this at build time) ─
build/includes/ build/includes/
build/live-includes/
build/first-login.sh build/first-login.sh
# ── Build artifacts ──────────────────────────────────────────────────── # ── Build artifacts ────────────────────────────────────────────────────

View File

@@ -8,7 +8,6 @@ base-system
linux linux
linux-firmware linux-firmware
linux-firmware-network linux-firmware-network
intel-ucode
dracut dracut
# --- core userspace --- # --- core userspace ---
@@ -61,15 +60,20 @@ xorg-fonts
xorg-input-drivers xorg-input-drivers
xf86-input-libinput xf86-input-libinput
xf86-video-intel xf86-video-intel
xf86-video-fbdev
xf86-video-vesa
mesa-dri mesa-dri
mesa-vulkan-intel mesa-vulkan-intel
intel-video-accel intel-video-accel
vulkan-loader vulkan-loader
pciutils
# --- nvidia (PRIME offload) --- # --- nvidia (PRIME offload, from nonfree repo) ---
# Installed so live-setup.sh can detect and configure PRIME at boot.
nvidia nvidia
nvidia-libs-32bit nvidia-libs
nvidia-vaapi-driver nvidia-vaapi-driver
# nvidia-vaapi-driver
# --- desktop --- # --- desktop ---
cinnamon cinnamon

206
docs/LIVE_ISO.md Normal file
View File

@@ -0,0 +1,206 @@
# Live ISO Build — Findings & Architecture Notes
## Overview
The live ISO boots directly into a Cinnamon desktop session as user `live` with no password prompt. It is designed for hardware testing on XPS 9700 and serves as the installer delivery vehicle.
Builder: `iso/build-live-iso.sh` (host) → Docker container running `iso/_inner-build-live.sh``void-mklive/mklive.sh`
---
## Boot + Session Startup
### Kernel Cmdline
```
live.user=live console=tty0 console=ttyS0,115200
```
The `live.user=live` parameter is consumed by the vmklive dracut hook (`adduser.sh`) which creates the user inside the initramfs and sets password `voidlinux`.
### runit Stage 2 Override
We override `/etc/runit/2` to run `/etc/runit/live-setup.sh` before handing off to `runsvdir`. The script:
1. Adds extra groups (`plugdev input network docker`) to the live user
2. Writes `/etc/sudoers.d/live` (full passwordless sudo)
3. Configures `/etc/nix/nix.conf` (daemon mode, `trusted-users = root live`)
4. Auto-detects GPU and writes `/etc/X11/xorg.conf.d/20-gpu.conf`
After live-setup.sh, stage 2 mirrors the real `runit-void` exactly:
```sh
runsvchdir "${runlevel}"
ln -sf /etc/runit/runsvdir/current /run/runit/runsvdir/current
exec runsvdir -P /run/runit/runsvdir/current
```
### Services (runsvdir/default symlinks in overlay)
Enabled at build time via symlinks in `build/live-includes/etc/runit/runsvdir/default/`:
- `dbus`
- `NetworkManager`
- `lightdm`
- `nix-daemon`
> **Note:** Do NOT use mklive.sh's `-S` flag for service enable — it is not supported by the version used. Services must be wired via runsvdir symlinks in the include overlay.
---
## LightDM Autologin
### Critical: `lightdm-session` does not exist on Void Linux
The Void `lightdm` 1.32 package does **not** ship the `lightdm-session` binary. The default LightDM behaviour of spawning `lightdm-session` causes the session to crash immediately (exit code 1 in ~20ms) with no error message.
**Fix:** Set `session-wrapper=/etc/lightdm/Xsession` in `lightdm.conf`. The `/etc/lightdm/Xsession` wrapper **is** provided by the Void lightdm package and correctly sources `/etc/profile``/etc/profile.d/`.
### `greeter-env=` and `session-env=` are not supported
These options are silently ignored in LightDM 1.32 on Void. To propagate environment variables to the session use `/etc/profile.d/` scripts instead.
### lightdm.conf autologin lines must be commented
The vmklive dracut hook `display-manager-autologin.sh` uses `sed` to uncomment lines. The autologin lines in `lightdm.conf` must be present but commented out — the hook finds them by regex and uncomments them at boot.
```ini
[Seat:*]
#autologin-user=
#autologin-user-timeout=0
#autologin-session=
#user-session=
session-wrapper=/etc/lightdm/Xsession
greeter-session=lightdm-gtk-greeter
```
The `/etc/lightdm/.session` file (content: `cinnamon`) is read by the hook to set the session name.
---
## GPU Auto-Detection
`live-setup.sh` runs `lspci` at boot and writes `/etc/X11/xorg.conf.d/20-gpu.conf`:
| Detected | Xorg Config | Extra |
|----------|-------------|-------|
| Virtual (virtio/VMware/QEMU/VirtualBox) | `modesetting`, `AccelMethod none` | `LIBGL_ALWAYS_SOFTWARE=1` in `/etc/profile.d/live-env.sh` |
| NVIDIA + proprietary driver (`nvidia_drv.so`) | PRIME offload: Intel `modesetting` + NVIDIA `nvidia` | No software GL |
| NVIDIA without proprietary driver | `modesetting` | — |
| Intel / AMD / other | `modesetting` | — |
`LIBGL_ALWAYS_SOFTWARE=1` is set via `/etc/profile.d/live-env.sh`, not via `session-env=` (unsupported).
---
## Nix Integration
### Daemon mode (not single-user)
The Void `nix` xbps package ships `nix-daemon` with a runit service at `/etc/sv/nix-daemon`. The daemon puts its socket at:
```
/var/nix/daemon-socket/socket
```
We use daemon mode (not single-user) because `/nix/store` stays root-owned. The live user is granted trust via `nix.conf`:
```
experimental-features = nix-command flakes
sandbox = false
auto-optimise-store = true
trusted-users = root live
```
`sandbox = false` is required because the live system has no `nixbld` users and no user namespaces in the dracut initramfs environment.
### Package list
`/usr/local/libexec/nix-packages.list` is written at ISO build time from `NIX_USER_PACKAGES` in `config/install.conf`. At first login, `first-login.sh` reads this file and runs `nix profile install --impure` with `NIXPKGS_ALLOW_UNFREE=1`.
Current packages:
- `nixpkgs#google-chrome`
- `nixpkgs#spotify`
- `nixpkgs#discord`
- `nixpkgs#localsend`
- `nixpkgs#mission-center`
### postinstall.sh socket path (installed system)
In the **installed system** (not live), `installer/lib/postinstall.sh` polls for the nix-daemon socket. The correct path is:
```
/var/nix/daemon-socket/socket
```
Not `/nix/var/nix/daemon-socket/socket` (upstream Nix default) — Void's package uses `/var/nix/`.
---
## dconf / Theme
The Gruvbox-Dark GTK theme and Cinnamon dconf settings are pre-applied via a system-db. The dconf binary database must be compiled at **ISO build time**, not at runtime.
### Build-time compilation
`iso/_inner-build-live.sh` runs inside the Debian Docker container. The Dockerfile installs `dconf-cli` for this step. The correct Debian `dconf-cli` API is:
```sh
dconf compile <output_binary_db> <input_keyfile_dir>
# e.g.:
dconf compile build/live-includes/etc/dconf/db/local \
build/live-includes/etc/dconf/db/local.d
```
> **Note:** `dconf update <path>` does not work in Debian's `dconf-cli` — it only updates the user's own db. `dconf compile` is the correct tool for building a system-db binary.
### dconf profile
`/etc/dconf/profile/user` must point to the system-db:
```
user-db:user
system-db:local
```
Without this file, the compiled system-db is ignored and Cinnamon shows a black wallpaper with default GTK theme.
---
## First-Login Setup (`installer/first-login.sh`)
Runs once via XDG autostart (`~/.config/autostart/void-live-first-login.desktop`) when Cinnamon first loads. Installs:
1. **Claude Code** — official installer from `https://claude.ai/install.sh`
2. **Nix user packages** — from `/usr/local/libexec/nix-packages.list`
3. **NVM + Node LTS**
4. **VS Code extensions** — from `/etc/installer-vscode-extensions.txt`
Idempotent: creates `~/.first-login-done` on success. Logs to `~/.first-login.log`.
The script does NOT use `set -u` because `nvm.sh` references unbound variables.
---
## Build Pipeline
```
iso/build-live-iso.sh (host — stages overlay, builds Docker image if needed)
└─ Docker: void-installer-builder:latest
└─ iso/_inner-build-live.sh
├─ dconf compile (pre-bakes system-db)
└─ void-mklive/mklive.sh -a x86_64 -r <repo> -I <include_dir> ...
└─ squashfs + GRUB + ISO 9660
```
Output: `out/void-live-stable.iso` (~2.9 GB)
### Build artifacts that must NOT be committed
- `build/live-includes/` — generated staging tree (hundreds of binary assets)
- `out/` — ISO output
- `cache/` — cloned void-mklive, xbps package cache
---
## QEMU Testing
```bash
cp /usr/share/OVMF/OVMF_VARS.fd out/OVMF_VARS.live.fd
qemu-system-x86_64 -name void-live-test -machine q35,accel=kvm:tcg -cpu max \
-m 4096 -smp 4 \
-drive "if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE.fd" \
-drive "if=pflash,format=raw,file=out/OVMF_VARS.live.fd" \
-cdrom out/void-live-stable.iso -boot order=d,menu=off \
-netdev user,id=n0 -device virtio-net-pci,netdev=n0 \
-serial "unix:out/live-serial.sock,server,nowait" \
-monitor "unix:out/qemu-monitor.sock,server,nowait" \
-device virtio-vga -display gtk,gl=off &
```
Serial console access (root shell for diagnostics):
```python
import socket, time
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect('out/live-serial.sock')
# send commands, read output
```
GPU in QEMU: `virtio-vga` is detected as virtual → `modesetting + LIBGL_ALWAYS_SOFTWARE=1`.

View File

@@ -1,6 +1,8 @@
#!/bin/bash #!/bin/bash
# First-login one-shot setup for the user. # First-login one-shot setup for the user.
# Installs Claude Code (official) + NVM + node LTS + vscode extensions. # Installs: Claude Code, NVM + node LTS, VS Code extensions,
# and (if NIX_PACKAGES_FILE is present) nix user packages
# (google-chrome, spotify, discord, localsend, mission-center).
# Idempotent: creates ~/.first-login-done marker on success. # Idempotent: creates ~/.first-login-done marker on success.
# NOTE: do NOT use `set -u` here — nvm.sh references unbound vars. # NOTE: do NOT use `set -u` here — nvm.sh references unbound vars.
@@ -19,8 +21,7 @@ if ! getent hosts github.com >/dev/null 2>&1; then
exit 0 exit 0
fi fi
# --- Claude Code (official native installer) — runs FIRST so failures in # --- Claude Code (official native installer) ---
# downstream NVM/node/etc. don't block claude installation. ---
mkdir -p "$HOME/.local/bin" mkdir -p "$HOME/.local/bin"
export PATH="$HOME/.local/bin:$PATH" export PATH="$HOME/.local/bin:$PATH"
if ! command -v claude >/dev/null 2>&1 && [[ ! -x "$HOME/.local/bin/claude" ]]; then if ! command -v claude >/dev/null 2>&1 && [[ ! -x "$HOME/.local/bin/claude" ]]; then
@@ -29,6 +30,39 @@ if ! command -v claude >/dev/null 2>&1 && [[ ! -x "$HOME/.local/bin/claude" ]];
echo "!! claude install failed"; } echo "!! claude install failed"; }
fi fi
# --- Nix user packages (google-chrome, spotify, discord, etc.) ---
# Present when running from the live ISO (written by build-live-iso.sh).
# In the installed system the packages come from first-boot-nix.sh instead.
NIX_PACKAGES_FILE="${NIX_PACKAGES_FILE:-/usr/local/libexec/nix-packages.list}"
if [[ -r "$NIX_PACKAGES_FILE" ]] && command -v nix >/dev/null 2>&1; then
echo "==> installing nix user packages from $NIX_PACKAGES_FILE"
# Source nix profile.d scripts so PATH and env are set.
for f in /etc/profile.d/nix*.sh; do
# shellcheck disable=SC1090
[[ -r "$f" ]] && . "$f"
done
# Initialise per-user nix profile if needed.
if [[ ! -d "$HOME/.nix-profile" ]]; then
nix-env --switch-profile "$HOME/.nix-profile" 2>/dev/null || true
fi
# D-Bus session is available when autostarted from Cinnamon.
if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then
eval "$(dbus-launch --sh-syntax 2>/dev/null)" || true
fi
export NIXPKGS_ALLOW_UNFREE=1
mapfile -t pkgs < <(grep -vE '^\s*(#|$)' "$NIX_PACKAGES_FILE")
if [[ ${#pkgs[@]} -gt 0 ]]; then
echo " packages: ${pkgs[*]}"
nix profile install --impure "${pkgs[@]}" 2>&1 || {
echo "!! nix profile install failed (partial install may have succeeded)"; }
fi
fi
# --- NVM (best effort; nvm.sh has unbound vars so isolate it) --- # --- NVM (best effort; nvm.sh has unbound vars so isolate it) ---
if [[ ! -s "$HOME/.nvm/nvm.sh" ]]; then if [[ ! -s "$HOME/.nvm/nvm.sh" ]]; then
echo "==> installing NVM" echo "==> installing NVM"
@@ -39,7 +73,6 @@ fi
if [[ -s "$HOME/.nvm/nvm.sh" ]]; then if [[ -s "$HOME/.nvm/nvm.sh" ]]; then
export NVM_DIR="$HOME/.nvm" export NVM_DIR="$HOME/.nvm"
# nvm.sh trips `set -u` on STABLE/PROVIDED_VERSION; isolate in subshell.
( (
set +u set +u
# shellcheck disable=SC1091 # shellcheck disable=SC1091
@@ -51,8 +84,6 @@ if [[ -s "$HOME/.nvm/nvm.sh" ]]; then
nvm use --lts >/dev/null 2>&1 || true nvm use --lts >/dev/null 2>&1 || true
) || true ) || true
# Symlink the resulting node/npm into ~/.local/bin so they're on PATH
# for non-nvm shells.
NODE_BIN_DIR="$(ls -d "$HOME"/.nvm/versions/node/v*/bin 2>/dev/null | sort -V | tail -1)" NODE_BIN_DIR="$(ls -d "$HOME"/.nvm/versions/node/v*/bin 2>/dev/null | sort -V | tail -1)"
if [[ -n "$NODE_BIN_DIR" && -d "$NODE_BIN_DIR" ]]; then if [[ -n "$NODE_BIN_DIR" && -d "$NODE_BIN_DIR" ]]; then
for bin in node npm npx; do for bin in node npm npx; do

View File

@@ -206,14 +206,13 @@ mark=/var/lib/first-boot-nix.done
[[ -f "\$mark" ]] && exit 0 [[ -f "\$mark" ]] && exit 0
# Wait for nix-daemon to be available. # Wait for nix-daemon to be available.
# The Void package puts the socket at /var/nix/daemon-socket/nix-daemon.sock # The Void xbps nix package puts the socket at /var/nix/daemon-socket/socket.
# (NOT /nix/var/nix/...). for _ in \$(seq 1 60); do
for _ in \$(seq 1 30); do [[ -S /var/nix/daemon-socket/socket ]] && break
[[ -S /var/nix/daemon-socket/nix-daemon.sock ]] && break sleep 2
sleep 1
done done
if [[ ! -S /var/nix/daemon-socket/nix-daemon.sock ]]; then if [[ ! -S /var/nix/daemon-socket/socket ]]; then
echo "nix-daemon not available; aborting first-boot nix install" >&2 echo "nix-daemon not available; aborting first-boot nix install" >&2
exit 0 exit 0
fi fi

View File

@@ -10,7 +10,7 @@ ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
bash git curl ca-certificates xz-utils tar patch python3 \ bash git curl ca-certificates xz-utils tar patch python3 \
mtools xorriso squashfs-tools dosfstools e2fsprogs \ mtools xorriso squashfs-tools dosfstools e2fsprogs \
kmod \ kmod dconf-cli \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# xbps-static is downloaded into /cache by the host script and added to PATH # 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")" mkdir -p "$(dirname "$OUT_ISO")"
# Compile dconf system-db inside the include dir so it ships compiled. # 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 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 compile "$INCLUDE_DIR/etc/dconf/db/local" \
DCONF_SYSTEM_DB_PATH="$INCLUDE_DIR/etc/dconf/db" \ "$INCLUDE_DIR/etc/dconf/db/local.d" 2>/dev/null \
dconf update "$INCLUDE_DIR/etc/dconf/db" 2>/dev/null || true && echo "dconf: compiled system-db/local" \
|| echo "dconf: compile failed (non-fatal)"
fi fi
cd "$MKLIVE_DIR" cd "$MKLIVE_DIR"
@@ -52,6 +54,7 @@ trap _cleanup_mklive_builds EXIT
./mklive.sh \ ./mklive.sh \
-a "$ARCH" \ -a "$ARCH" \
-r "$REPO_URL" \ -r "$REPO_URL" \
-r "${REPO_URL%/current}/current/nonfree" \
-c "$CACHE_DIR/xbps-live-pkgs" \ -c "$CACHE_DIR/xbps-live-pkgs" \
-H "$CACHE_DIR/xbps-host-pkgs" \ -H "$CACHE_DIR/xbps-host-pkgs" \
-k "$KEYMAP" \ -k "$KEYMAP" \

View File

@@ -70,57 +70,201 @@ rm -rf "$INCLUDE_DIR"
mkdir -p "$INCLUDE_DIR" mkdir -p "$INCLUDE_DIR"
# ── 3a) live user account ─────────────────────────────────────────────── # ── 3a) live user account ───────────────────────────────────────────────
# Create user 'live' with no password in the live image. # The vmklive dracut hook (adduser.sh) creates the live user in initramfs
# mklive populates /etc/passwd at install time — we drop a post-install hook # using the live.user= kernel cmdline parameter (added to BOOT_CMDLINE below).
# via the live init that calls useradd. We use an install.conf.d override # We add extra groups and sudo via /etc/runit/2 override (after pivot_root).
# that mklive will source, plus an rc.local-style runit oneshot.
install -d -m 0755 "$INCLUDE_DIR/etc" 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. # GPU detection + extra group setup — runs from our runit/2 override below.
install -d -m 0755 "$INCLUDE_DIR/etc/runit/runsvdir/default" install -d -m 0755 "$INCLUDE_DIR/etc/runit"
install -d -m 0755 "$INCLUDE_DIR/etc/sv/live-user-setup" cat > "$INCLUDE_DIR/etc/runit/live-setup.sh" <<'SV_EOF'
cat > "$INCLUDE_DIR/etc/sv/live-user-setup/run" <<'SV_EOF'
#!/bin/sh #!/bin/sh
# Create the 'live' user (no password) and set up home directory. # Runs from /etc/runit/2 before any service starts.
# Runs once; removes itself from default runsvdir on completion. # 1. Ensures live user has all needed groups.
set -e # 2. Generates /etc/X11/xorg.conf.d/20-gpu.conf based on detected hardware.
if ! id live >/dev/null 2>&1; then
useradd -m -s /bin/bash -G wheel,audio,video,plugdev,input,network,docker live LIVE_USER="${USERNAME:-live}"
passwd -d live [ -f /etc/default/live.conf ] && . /etc/default/live.conf
install -d -m 0700 /home/live/.ssh LIVE_USER="${USERNAME:-live}"
# Apply skel dotfiles
if [ -d /etc/skel ]; then # Extra groups (dracut only adds audio,video,wheel)
cp -a /etc/skel/. /home/live/ 2>/dev/null || true for g in plugdev input network docker; do
fi groupadd -f "$g" 2>/dev/null || true
chown -R live:live /home/live 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 fi
# Enable sudoers entry for live NIXSH
if ! grep -q '^live ' /etc/sudoers 2>/dev/null; then chmod 0644 /etc/profile.d/nix-live.sh
echo 'live ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers 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 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 SV_EOF
chmod 0755 "$INCLUDE_DIR/etc/sv/live-user-setup/run" chmod 0755 "$INCLUDE_DIR/etc/runit/live-setup.sh"
ln -sf /etc/sv/live-user-setup "$INCLUDE_DIR/etc/runit/runsvdir/default/live-user-setup"
# 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 ─────────────────────────────────────────────── # ── 3b) LightDM autologin ───────────────────────────────────────────────
install -d -m 0755 "$INCLUDE_DIR/etc/lightdm" 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:*] [Seat:*]
autologin-user=${LIVE_USER} #autologin-user=
autologin-user-timeout=0 #autologin-user-timeout=0
user-session=cinnamon #autologin-session=
#user-session=
session-wrapper=/etc/lightdm/Xsession
greeter-session=lightdm-gtk-greeter greeter-session=lightdm-gtk-greeter
EOF 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" install -d -m 0755 "$INCLUDE_DIR/etc/runit/runsvdir/default"
# Enable NetworkManager, lightdm, pipewire and dbus for the live session. # Enable services for the live session.
for svc in dbus NetworkManager lightdm; do 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 ln -sf "/etc/sv/$svc" "$INCLUDE_DIR/etc/runit/runsvdir/default/$svc" 2>/dev/null || true
done done
@@ -285,6 +429,20 @@ case ":$PATH:" in
esac esac
EOF 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 ──────── # ── 3g) Live-session skel: pre-wire .bash_profile for first-login ────────
install -d -m 0755 "$INCLUDE_DIR/etc/skel" install -d -m 0755 "$INCLUDE_DIR/etc/skel"
cat > "$INCLUDE_DIR/etc/skel/.bash_profile" <<'EOF' cat > "$INCLUDE_DIR/etc/skel/.bash_profile" <<'EOF'
@@ -313,6 +471,9 @@ ISO_PKGS=$(grep -vE '^\s*(#|$)' \
| tr '\n' ' ') | tr '\n' ' ')
TS="$(date -u +%Y%m%d)" TS="$(date -u +%Y%m%d)"
OUT_ISO="${OUTPUT_ISO:-$OUT_DIR/void-live-stable-${TS}.iso}" 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" echo ">>> running mklive.sh inside docker — output: $OUT_ISO"
"$DOCKER" run --rm --privileged \ "$DOCKER" run --rm --privileged \