diff --git a/.gitignore b/.gitignore index b7ed45d..1c4d6ec 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ authorized_keys # ── Generated build staging (build-iso.sh populates this at build time) ─ build/includes/ +build/live-includes/ build/first-login.sh # ── Build artifacts ──────────────────────────────────────────────────── diff --git a/config/profiles/stable-cinnamon/packages.live-desktop.list b/config/profiles/stable-cinnamon/packages.live-desktop.list index 8b40766..6c033c4 100644 --- a/config/profiles/stable-cinnamon/packages.live-desktop.list +++ b/config/profiles/stable-cinnamon/packages.live-desktop.list @@ -8,7 +8,6 @@ base-system linux linux-firmware linux-firmware-network -intel-ucode dracut # --- core userspace --- @@ -61,15 +60,20 @@ xorg-fonts xorg-input-drivers xf86-input-libinput xf86-video-intel +xf86-video-fbdev +xf86-video-vesa mesa-dri mesa-vulkan-intel intel-video-accel 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-libs-32bit +nvidia-libs nvidia-vaapi-driver +# nvidia-vaapi-driver # --- desktop --- cinnamon diff --git a/docs/LIVE_ISO.md b/docs/LIVE_ISO.md new file mode 100644 index 0000000..f86b0e7 --- /dev/null +++ b/docs/LIVE_ISO.md @@ -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 +# e.g.: +dconf compile build/live-includes/etc/dconf/db/local \ + build/live-includes/etc/dconf/db/local.d +``` + +> **Note:** `dconf update ` 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 -I ... + └─ 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`. diff --git a/installer/first-login.sh b/installer/first-login.sh index e31e50f..70857a0 100644 --- a/installer/first-login.sh +++ b/installer/first-login.sh @@ -1,6 +1,8 @@ #!/bin/bash # 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. # 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 fi -# --- Claude Code (official native installer) — runs FIRST so failures in -# downstream NVM/node/etc. don't block claude installation. --- +# --- Claude Code (official native installer) --- mkdir -p "$HOME/.local/bin" export PATH="$HOME/.local/bin:$PATH" 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"; } 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) --- if [[ ! -s "$HOME/.nvm/nvm.sh" ]]; then echo "==> installing NVM" @@ -39,7 +73,6 @@ fi if [[ -s "$HOME/.nvm/nvm.sh" ]]; then export NVM_DIR="$HOME/.nvm" - # nvm.sh trips `set -u` on STABLE/PROVIDED_VERSION; isolate in subshell. ( set +u # shellcheck disable=SC1091 @@ -51,8 +84,6 @@ if [[ -s "$HOME/.nvm/nvm.sh" ]]; then nvm use --lts >/dev/null 2>&1 || 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)" if [[ -n "$NODE_BIN_DIR" && -d "$NODE_BIN_DIR" ]]; then for bin in node npm npx; do diff --git a/installer/lib/postinstall.sh b/installer/lib/postinstall.sh index 158faa1..e1805df 100755 --- a/installer/lib/postinstall.sh +++ b/installer/lib/postinstall.sh @@ -206,14 +206,13 @@ mark=/var/lib/first-boot-nix.done [[ -f "\$mark" ]] && exit 0 # Wait for nix-daemon to be available. -# The Void package puts the socket at /var/nix/daemon-socket/nix-daemon.sock -# (NOT /nix/var/nix/...). -for _ in \$(seq 1 30); do - [[ -S /var/nix/daemon-socket/nix-daemon.sock ]] && break - sleep 1 +# The Void xbps nix package puts the socket at /var/nix/daemon-socket/socket. +for _ in \$(seq 1 60); do + [[ -S /var/nix/daemon-socket/socket ]] && break + sleep 2 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 exit 0 fi diff --git a/iso/Dockerfile b/iso/Dockerfile index a370d35..0665aef 100644 --- a/iso/Dockerfile +++ b/iso/Dockerfile @@ -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 diff --git a/iso/_inner-build-live.sh b/iso/_inner-build-live.sh index cd9e5fe..8f040b4 100755 --- a/iso/_inner-build-live.sh +++ b/iso/_inner-build-live.sh @@ -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 '. 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" \ diff --git a/iso/build-live-iso.sh b/iso/build-live-iso.sh index 10f4a47..12ba410 100755 --- a/iso/build-live-iso.sh +++ b/iso/build-live-iso.sh @@ -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" < "$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 < /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 < /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" < "$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 \