sway ARRANCA EN QEMU con pantalla — evidencia, y seis muros que el grafo no podía ver
`docs/evidencia/sway-qemu-2026-08-08.png`: 1280×800, **102 colores distintos**, sway con foot a pantalla completa y un prompt vivo, la barra de título del compositor arriba y el fondo #1a4b8c asomando en los bordes. Sobre el kernel de hammer con arje-zero como PID1, sin X11 y sin systemd. EL VEREDICTO ES CONTAR COLORES, NO LEER EL LOG — y esta campaña lo demuestra sola: hubo un arranque con TODO verde en el log (salida activada, modo 1280x800, «Commit of 1 outputs succeeded», workspace creado) y la captura salía **100% negra**. Un compositor puede estar perfectamente vivo y no pintar nada. LOS SEIS MUROS, ninguno visible en un grafo de dependencias que decía 121/121: 1. `PATH` vacío — el console-getty no lo exporta y ni `mkdir` se encontraba. «mkdir: not found» se lee como «falta busybox» cuando busybox está entero: mismo engaño que el exit 127 de meson. 2. `libz.so.1` AUSENTE del rootfs. El zlib del corpus es sólo estático; la compartida vive en `zlib-shared`, en otra cola. Se cazó comparando los NEEDED del ELF contra el rootfs, no leyendo logs. 3. `LIBSEAT_BACKEND=builtin` NO EXISTE en nuestro libseat: `recipes/seatd.toml` construye con `-Dlibseat-seatd=enabled -Dlibseat-logind=disabled`, o sea UN backend, confirmado con `strings`. La receta manda sobre lo que uno cree recordar del proyecto — y la misma receta traía la salida: `-Dserver=enabled` construye `seatd-launch`. 4. `/run` de SÓLO LECTURA. La raíz de hammer es inmutable por diseño, y sway moría con «unable to open lockfile … check permissions», que suena a permisos de directorio y era un filesystem read-only. Se resolvió montando un tmpfs, que es lo que hace cualquier init. 5. `xkeyboard-config` — «failed to add default include path /usr/share/X11/xkb». Es EXACTAMENTE lo que dejé anotado en la receta de swaylock: «para que el teclado tenga distribución en una imagen real hará falta incluirlo por el lado del perfil». Apareció donde se dijo. 6. SIN TIPOGRAFÍAS no hay terminal. `fcft: failed to match font` → `failed to load primary fonts`: foot arrancaba y no dibujaba. `dejavu-fonts` estaba en el catálogo pero en otra cola. Va en la lista de §I.5 del SDD 20 («tipografías») y acá se ve por qué no es un detalle. Y un fallo de MÉTODO que costó dos ciclos: mandé el log de sway a /var/log/sway.log DENTRO de la VM, así que la primera captura negra vino sin una sola línea que la explicara — el diagnóstico estaba dentro de la caja que no arranca. Un log que no se puede leer desde fuera no es un log. Ahora va al serial. Los `exec` de la config de sway disparan ANTES de que exista el workspace (0,259 s vs 0,305 s) y mueren con «Failed to create launch context. No workspace». Los clientes se lanzan desde sway-start esperando el socket de Wayland, que es la condición real. Andamiaje reutilizable en scripts/wlr/: qemu-sway-image.sh (hermano del de COSMIC, sin logind ni dbus, que sway no necesita), sway-start.sh y sway-config. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
Executable
+92
@@ -0,0 +1,92 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# qemu-sway-image.sh — imagen QEMU del perfil escritorio-sway. Hermano de
|
||||||
|
# scripts/cosmic/qemu-desktop-image.sh: MISMO andamiaje probado (base metal + cierre hidratado +
|
||||||
|
# mesa-llvmpipe + musl), sin las piezas que COSMIC necesita y sway no.
|
||||||
|
#
|
||||||
|
# QUÉ SE CAE respecto del de COSMIC, y por qué:
|
||||||
|
# · arje-logind-compat — sway toma el DRM con `LIBSEAT_BACKEND=builtin` siendo root, así que no
|
||||||
|
# hace falta un proveedor de login1. Una pieza móvil menos.
|
||||||
|
# · dbus / messagebus / setuid helper — nada del entorno mínimo de sway lo pide.
|
||||||
|
# · catálogo AppStream — era para cosmic-store; acá no hay tienda.
|
||||||
|
# Se conserva TODO lo que ya costó caro descubrir: la inyección de kms_swrast (en QEMU no hay GPU),
|
||||||
|
# libLLVM.18 (llvmpipe lo JIT-linkea), el runtime C++ de Alpine (deuda conocida de la cadena de
|
||||||
|
# mesa) y los DOS sonames de musl (zig-cc emite `libc.so`, el gcc de Alpine
|
||||||
|
# `libc.musl-x86_64.so.1`: es la MISMA libc y sin el segundo symlink lo de gcc no carga).
|
||||||
|
set -eu
|
||||||
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
|
||||||
|
|
||||||
|
BASE="${BASE:-work/metal-rootfs}"
|
||||||
|
SWAY="${SWAY:-work/sway-rootfs}"
|
||||||
|
MESA="${MESA:-$(ls -dt store/*-mesa-llvmpipe 2>/dev/null | head -1)}"
|
||||||
|
LLVM="${LLVM:-$(ls -dt store/*-llvm18 2>/dev/null | head -1)}"
|
||||||
|
MERGED="${MERGED:-work/sway-qemu-rootfs}"
|
||||||
|
IMG="${IMG:-work/hammer-sway-qemu.img}"
|
||||||
|
|
||||||
|
[ -d "$BASE" ] || { echo "falta la base metal: $BASE" >&2; exit 1; }
|
||||||
|
[ -d "$SWAY" ] || { echo "falta el cierre sway: $SWAY (correr el hydrate)" >&2; exit 1; }
|
||||||
|
|
||||||
|
echo "==> fundiendo rootfs por hardlinks (base metal + cierre sway)"
|
||||||
|
rm -rf "$MERGED"
|
||||||
|
cp -al "$BASE" "$MERGED"
|
||||||
|
cp -al --remove-destination "$SWAY"/. "$MERGED"/
|
||||||
|
|
||||||
|
. "$ROOT/scripts/lib/pid1-desde-store.sh"
|
||||||
|
inyectar_pid1 "$MERGED" "$BASE"
|
||||||
|
|
||||||
|
echo "==> parcheando mesa software desde $(basename "$MESA")"
|
||||||
|
mkdir -p "$MERGED"/usr/lib/dri
|
||||||
|
cp -a --remove-destination "$MESA"/usr/lib/dri/. "$MERGED"/usr/lib/dri/ 2>/dev/null || true
|
||||||
|
for so in libglapi libgbm libEGL libGLESv2 libGL libgallium; do
|
||||||
|
cp -a --remove-destination "$MESA"/usr/lib/$so* "$MERGED"/usr/lib/ 2>/dev/null || true
|
||||||
|
done
|
||||||
|
ls "$MERGED"/usr/lib/dri/*swrast* >/dev/null 2>&1 && echo " ✓ kms_swrast instalado" || { echo " ✗ swrast no entró" >&2; exit 1; }
|
||||||
|
|
||||||
|
echo "==> inyectando libLLVM.18 (llvmpipe lo JIT-linkea)"
|
||||||
|
[ -n "$LLVM" ] && [ -d "$LLVM" ] || { echo " ✗ falta llvm18 en store/" >&2; exit 1; }
|
||||||
|
cp -a --remove-destination "$LLVM"/usr/lib/libLLVM.so.18.1 "$MERGED"/usr/lib/
|
||||||
|
ln -sf libLLVM.so.18.1 "$MERGED"/usr/lib/libLLVM.so.18
|
||||||
|
ln -sf libLLVM.so.18.1 "$MERGED"/usr/lib/libLLVM.so
|
||||||
|
|
||||||
|
echo "==> runtime C++ de Alpine (deuda conocida de la cadena de mesa)"
|
||||||
|
for lib in libgcc_s.so.1 libstdc++.so.6; do
|
||||||
|
src=".dev-fs/alpine/usr/lib/$lib"
|
||||||
|
[ -r "$src" ] && cp -a --remove-destination "$src"* "$MERGED"/usr/lib/ 2>/dev/null || echo " ⚠ sin $lib"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "==> musl y los DOS sonames"
|
||||||
|
MUSL="${MUSL:-/usr/lib/musl/lib/libc.so}"
|
||||||
|
[ -r "$MUSL" ] || { echo "no encuentro musl libc ($MUSL)" >&2; exit 1; }
|
||||||
|
mkdir -p "$MERGED/usr/lib/musl/lib" "$MERGED/lib"
|
||||||
|
cp -a --remove-destination "$MUSL" "$MERGED/usr/lib/musl/lib/libc.so"
|
||||||
|
ln -sf /usr/lib/musl/lib/libc.so "$MERGED/lib/ld-musl-x86_64.so.1"
|
||||||
|
ln -sf /usr/lib/musl/lib/libc.so "$MERGED/usr/lib/libc.so"
|
||||||
|
|
||||||
|
echo "==> grupo video (wlroots abre /dev/dri)"
|
||||||
|
grep -q '^video:' "$MERGED/etc/group" 2>/dev/null || { { cat "$MERGED/etc/group"; echo "video:x:44:"; } > "$MERGED/etc/group.new"; chmod 644 "$MERGED/etc/group.new"; mv "$MERGED/etc/group.new" "$MERGED/etc/group"; }
|
||||||
|
|
||||||
|
echo "==> instalando /usr/bin/sway-start y la config"
|
||||||
|
install -Dm755 scripts/wlr/sway-start.sh "$MERGED/usr/bin/sway-start"
|
||||||
|
install -Dm644 scripts/wlr/sway-config "$MERGED/etc/sway/config"
|
||||||
|
|
||||||
|
echo "==> auto-lanzar sway: console-getty ejecuta sway-start (no /bin/sh)"
|
||||||
|
python3 - "$MERGED/ente/seed.card.json" <<'PY'
|
||||||
|
import json, sys, os
|
||||||
|
p = sys.argv[1]; c = json.load(open(p))
|
||||||
|
for g in c.get("genesis", []):
|
||||||
|
if g.get("label") == "console-getty":
|
||||||
|
argv = g["payload"]["Native"]["argv"]
|
||||||
|
argv[:] = [("/usr/bin/sway-start" if a == "/bin/sh" else a) for a in argv]
|
||||||
|
print(" console-getty argv →", argv)
|
||||||
|
tmp = p + ".new"
|
||||||
|
json.dump(c, open(tmp, "w"), indent=2); os.chmod(tmp, 0o644); os.replace(tmp, p)
|
||||||
|
PY
|
||||||
|
|
||||||
|
ln -sfn /run "$MERGED/var/run"
|
||||||
|
mkdir -p "$MERGED/root/.config" "$MERGED/run/user/0" "$MERGED/tmp" "$MERGED/var/log"
|
||||||
|
|
||||||
|
echo "==> rootfs fundido: $(du -sh "$MERGED" | cut -f1)"
|
||||||
|
need_mb=$(du -sm "$MERGED" | cut -f1)
|
||||||
|
ROOT_SIZE="${ROOT_SIZE:-$(( need_mb + 1536 ))}"
|
||||||
|
export ROOTFS="$MERGED" IMG ROOT_SIZE
|
||||||
|
export ESP_MB="${ESP_MB:-128}" STORE_SIZE="${STORE_SIZE:-512}" STATE_SIZE="${STATE_SIZE:-768}"
|
||||||
|
exec ./scripts/install-image-efi.sh
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Config mínima para la prueba en QEMU. Los clientes NO se lanzan desde acá: los `exec` de la config
|
||||||
|
# disparan ANTES de que exista el workspace y mueren con «Failed to create launch context». Los
|
||||||
|
# lanza sway-start cuando el socket de Wayland ya escucha. Ver el comentario allí.
|
||||||
|
output * bg #1a4b8c solid_color
|
||||||
Executable
+75
@@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# sway-start.sh — arranque de sway dentro de la VM. Lo ejecuta el `console-getty` de arje-zero en
|
||||||
|
# lugar de /bin/sh, así que corre como PID de sesión sin login manager.
|
||||||
|
#
|
||||||
|
# ── LA SESIÓN VA POR `seatd-launch`, NO POR EL BACKEND «builtin» ───────────────────────────────
|
||||||
|
# Primer intento: `LIBSEAT_BACKEND=builtin`, que en libseat abre el DRM directo siendo root.
|
||||||
|
# **Falló, y el log lo dijo exacto**:
|
||||||
|
# [libseat] No backend matched name 'builtin'
|
||||||
|
# [backend/session] Unable to create seat: Invalid argument → Failed to start a DRM session
|
||||||
|
# Porque NUESTRO libseat no lo trae: `recipes/seatd.toml` construye con `-Dlibseat-seatd=enabled
|
||||||
|
# -Dlibseat-logind=disabled`, o sea UN solo backend. Confirmado con `strings libseat.so`: lista
|
||||||
|
# `seatd` y nada más.
|
||||||
|
# ⇒ La receta manda sobre lo que uno cree recordar de un proyecto. Y la misma receta trae la
|
||||||
|
# solución: `-Dserver=enabled` construye el demonio Y `seatd-launch`, que lo levanta, corre el
|
||||||
|
# comando y limpia al salir.
|
||||||
|
# ── tmpfs SOBRE /run: la raíz de hammer se monta de SÓLO LECTURA ───────────────────────────────
|
||||||
|
# El diagnóstico lo dijo sin ambigüedad: «/run/user/0 NO escribible». `/run` no es un tmpfs sino un
|
||||||
|
# directorio normal sobre la raíz, y la raíz de esta distro es INMUTABLE por diseño (root ro +
|
||||||
|
# partición de estado aparte). Sin esto, sway no puede crear su lockfile y muere en bucle con
|
||||||
|
# «unable to open lockfile /run/user/0/wayland-N.lock check permissions» — un mensaje que suena a
|
||||||
|
# permisos de directorio y en realidad es un sistema de ficheros de sólo lectura.
|
||||||
|
# Montar un tmpfs es lo que hace cualquier init; acá lo hace el lanzador porque arje-zero deja /run
|
||||||
|
# para quien lo necesite.
|
||||||
|
BB=/bin/busybox
|
||||||
|
$BB mount -t tmpfs -o mode=755 tmpfs /run 2>/dev/null || true
|
||||||
|
|
||||||
|
export XDG_RUNTIME_DIR=/run/user/0
|
||||||
|
$BB mkdir -p "$XDG_RUNTIME_DIR"; $BB chmod 700 "$XDG_RUNTIME_DIR"
|
||||||
|
|
||||||
|
# ── RENDERER PIXMAN, NO GLES2: en QEMU no hay driver de GPU que mesa pueda cargar ──────────
|
||||||
|
# Con gles2 el arranque muere: «MESA-LOADER: failed to open virtio_gpu: /usr/lib/dri/virtio_gpu_dri.so
|
||||||
|
# No such file or directory» — mesa busca el driver del dispositivo REAL que ve (virtio_gpu) y
|
||||||
|
# nuestro mesa-llvmpipe sólo trae swrast/kms_swrast. Y «Not allowed to force software rendering when
|
||||||
|
# API explicitly selects a hardware device»: LIBGL_ALWAYS_SOFTWARE no lo salva porque wlroots ya
|
||||||
|
# eligió el dispositivo EGL explícitamente. Las dos variables juntas se pedían cosas contradictorias.
|
||||||
|
# wlroots trae un renderer PIXMAN por software que NO pasa por EGL ni GBM (buffers dumb del DRM) y
|
||||||
|
# se construye SIEMPRE: -Drenderers sólo elige entre gles2 y vulkan. Es la salida para una VM sin GPU.
|
||||||
|
export WLR_RENDERER=pixman
|
||||||
|
|
||||||
|
# Diagnóstico: sin esto, cuando algo falla el log dice «no outputs» y nada más.
|
||||||
|
export WLR_DRM_NO_ATOMIC=1
|
||||||
|
export WAYLAND_DEBUG=0
|
||||||
|
|
||||||
|
# ⚠ EL LOG VA AL SERIAL, NO A UN FICHERO DENTRO DE LA VM. El primer intento lo mandaba a
|
||||||
|
# /var/log/sway.log y la captura salió 99% negra SIN UNA SOLA LÍNEA que explicara por qué: el
|
||||||
|
# diagnóstico estaba dentro de la caja que no arranca. Un log que no se puede leer desde fuera no
|
||||||
|
# es un log. (La causa resultó ser `libz.so.1` ausente del rootfs, invisible en la pantalla negra.)
|
||||||
|
# ── DIAGNÓSTICO EXPLÍCITO antes de arrancar ────────────────────────────────────────────────────
|
||||||
|
# sway falló con «unable to open lockfile /run/user/0/wayland-N.lock check permissions» y yo no
|
||||||
|
# tenía forma de saber si el directorio existía, de quién era, ni si /run era escribible. Adivinar
|
||||||
|
# cuesta un ciclo de imagen+arranque (~3 min); imprimir la verdad cuesta cuatro líneas.
|
||||||
|
echo "== id: $($BB id 2>&1)"
|
||||||
|
echo "== /run: $($BB ls -ld /run 2>&1)"
|
||||||
|
echo "== /run/user/0: $($BB ls -ld /run/user/0 2>&1)"
|
||||||
|
if $BB touch /run/user/0/.probe 2>/dev/null; then echo "== /run/user/0 ESCRIBIBLE"; $BB rm -f /run/user/0/.probe
|
||||||
|
else echo "== /run/user/0 NO escribible"; fi
|
||||||
|
|
||||||
|
$BB rm -f /run/seatd.sock # el getty reinicia en bucle si sway falla; un socket huérfano impide arrancar seatd
|
||||||
|
# ── LOS CLIENTES SE LANZAN CUANDO EL SOCKET YA ESCUCHA, NO DESDE LA CONFIG ─────────────────────
|
||||||
|
# Los `exec` del fichero de config de sway dispararon a los 0,259 s y el workspace se creó a los
|
||||||
|
# 0,305 s ⇒ los tres murieron con «Failed to create launch context. No workspace». Resultado: el
|
||||||
|
# compositor arrancaba PERFECTO —salida activada, modo 1280x800, commit OK— y la captura salía 100%
|
||||||
|
# negra, porque no había un solo cliente dibujando. Es el peor tipo de fallo: todo verde en el log
|
||||||
|
# y nada en la pantalla, que es exactamente por lo que la regla dice validar CON PANTALLA.
|
||||||
|
# Acá se espera al socket de Wayland y recién entonces se lanza, que es la condición real.
|
||||||
|
(
|
||||||
|
i=0
|
||||||
|
while [ ! -S /run/user/0/wayland-1 ] && [ $i -lt 60 ]; do $BB sleep 1; i=$((i+1)); done
|
||||||
|
export WAYLAND_DISPLAY=wayland-1
|
||||||
|
/usr/bin/swaybg -c '#1a4b8c' &
|
||||||
|
/usr/bin/yambar &
|
||||||
|
/usr/bin/foot &
|
||||||
|
) &
|
||||||
|
|
||||||
|
exec /usr/bin/seatd-launch -- /usr/bin/sway -d 2>&1
|
||||||
Reference in New Issue
Block a user