gnome: la cima ARRANCA en QEMU — muere en meta_launcher_new, causa localizada
gnome-shell bootea, compila sus esquemas, toma el DRM de virtio-gpu y se declara Wayland display server. Después SIGSEGV. El backtrace del core no deja dudas: #4 g_variant_get (value=0x0, "(s&o)") ← GVariant NULO #5 get_seat_proxy src/backends/meta-launcher.c:407 #6 meta_launcher_new (META_LAUNCHER_FLAG_TAKE_CONTROL) Mutter pide la propiedad `Seat` del objeto **Session** de logind — `(s&o)` es su firma estándar (nombre_del_seat, object_path). arje-logind-compat adquiere org.freedesktop.login1 y sirve el Manager, pero NO expone un objeto Session con esa propiedad ⇒ la lectura devuelve NULL y mutter desreferencia sin chequear. Que mutter no valide es fragilidad suya; el hueco es nuestro. Cinco eslabones hubo que armar antes de llegar a ese muro, ninguno anotado: 1. gschemas.compiled NO se genera con DESTDIR seteado (meson lo dice en el log del build) ⇒ GSettings abortaba en el primer g_settings_new(). 2. GI_TYPELIB_PATH tiene que incluir /usr/lib/gnome-shell: St/Shell/Gvc/Shew se instalan aparte por ser privados del shell. Es el env sin análogo en KDE. 3. /var/run no existía en la base metal; arje-logind-compat busca el bus en la ruta legacy y sin el symlink se iba a "modo idle". 4. La política D-Bus de login1 faltaba (system.conf trae <deny own="*"/> y normalmente la instala systemd). PERTENECE al artefacto de arje: está en el script de imagen sólo para dejar visible qué falta empaquetar. 5. /run/systemd/{sessions,seats,users} vacíos — libelogind resuelve la C-ABI sd-login LEYÉNDOLOS y nadie los escribe (el Announce de arje-logind-compat al bus del fractal falla con "identity mismatch"). gnome-start los escribe A MANO y está MARCADO COMO ANDAMIO. Con ese puente desaparece el "Failed to find any matching session". Gotcha que costó una iteración: `kill -0` TIENE ÉXITO sobre un zombi (el padre no lo cosechó todavía), así que el script reportaba "sin wayland-0 tras 45s" cuando el shell había muerto en el primer segundo. Mirando State: de /proc/<pid>/status sale el código real, 139. Todo el ciclo (hidratar → imagen → bootear → sacar el core con debugfs → gdb) y la lista de lo que falta quedan en docs/runbooks/gnome-qemu-desktop.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# Runbook — arrancar gnome-shell en QEMU (y dónde está el muro hoy)
|
||||
|
||||
Estado al 2026-07-29: **gnome-shell arranca, toma el rol de display server Wayland y muere de
|
||||
SIGSEGV en `meta_launcher_new`**. La causa está localizada con precisión (ver abajo); no es un
|
||||
misterio pendiente de diagnosticar, es una pieza de arje que falta implementar.
|
||||
|
||||
Hermano de [`kde-qemu-desktop.md`](kde-qemu-desktop.md), del que reusa el andamiaje: la base
|
||||
`work/metal-rootfs`, la inyección de mesa-llvmpipe/LLVM/musl, el truco del console-getty y el propio
|
||||
`scripts/kde/run-qemu-desktop.sh`.
|
||||
|
||||
## Ciclo completo
|
||||
|
||||
```sh
|
||||
cd ~/hammer
|
||||
bash scripts/gnome/hydrate-gnome.sh # cierre de gnome-shell → work/gnome-rootfs (108/108)
|
||||
bash scripts/gnome/qemu-desktop-image.sh # funde base+gnome, parchea, → work/hammer-gnome-qemu.img
|
||||
IMG=work/hammer-gnome-qemu.img SERIAL=work/gnome-qemu-serial.log MON=work/gnome-monitor.sock \
|
||||
TIMEOUT=150 DISP=none bash scripts/kde/run-qemu-desktop.sh
|
||||
grep -a "gnome-qemu ::" work/gnome-qemu-serial.log | awk '!seen[$0]++'
|
||||
```
|
||||
|
||||
`DISP=gtk` para ver la ventana (necesita `DISPLAY=:0 GDK_BACKEND=x11`). **Regla heredada de KDE:
|
||||
validar escritorios con PANTALLA, nunca sólo por serial** — pero hoy no hace falta, porque no llega
|
||||
a pintar.
|
||||
|
||||
## Sacar el core y el backtrace
|
||||
|
||||
El `gnome-start` deja el core en la raíz ext4 y hace `sync`, así que sobrevive al SIGKILL de QEMU.
|
||||
|
||||
```sh
|
||||
sfdisk -J work/hammer-gnome-qemu.img # localizar hammer-root (part 2)
|
||||
dd if=work/hammer-gnome-qemu.img of=/tmp/root.img bs=1M skip=129 count=5113 status=none
|
||||
debugfs -R "dump /core.gnome-shell.<pid> /tmp/core.gnome-shell" /tmp/root.img
|
||||
gdb -batch -q -ex "set sysroot $PWD/work/gnome-qemu-rootfs" \
|
||||
-ex "core-file /tmp/core.gnome-shell" -ex "bt 25" work/gnome-qemu-rootfs/usr/bin/gnome-shell
|
||||
```
|
||||
|
||||
## EL MURO DE HOY — y por qué no es un bug de mutter que haya que rodear
|
||||
|
||||
```
|
||||
#4 g_variant_get (value=0x0, format_string="(s&o)") ← GVariant NULO
|
||||
#5 get_seat_proxy (...) at src/backends/meta-launcher.c:407
|
||||
#6 meta_launcher_new (flags=META_LAUNCHER_FLAG_TAKE_CONTROL) at meta-launcher.c:464
|
||||
...
|
||||
#15 main () at src/main.c:664
|
||||
```
|
||||
|
||||
Mutter pide la propiedad `Seat` del objeto **Session** de logind — el `(s&o)` es la firma estándar
|
||||
de esa propiedad: `(nombre_del_seat, object_path_del_seat)`. `arje-logind-compat` adquiere
|
||||
`org.freedesktop.login1` y sirve el **Manager**, pero no expone un objeto Session con esa propiedad,
|
||||
así que la lectura devuelve NULL. Mutter no chequea y desreferencia ⇒ SIGSEGV.
|
||||
|
||||
Que mutter no valide es una fragilidad suya, pero **el hueco es nuestro**: falta el objeto Session.
|
||||
|
||||
### La cadena completa que hubo que armar para llegar hasta acá
|
||||
|
||||
Cada eslabón se descubrió arrancando la imagen, y ninguno estaba anotado antes:
|
||||
|
||||
1. **`gschemas.compiled` no existe.** meson NO lo genera cuando `DESTDIR` está seteado (lo dice en
|
||||
el log del build: *"Skipping custom install script because DESTDIR is set"*). Sin él GSettings
|
||||
aborta en el primer `g_settings_new()`. Se compila en el primer arranque.
|
||||
2. **`GI_TYPELIB_PATH` con `/usr/lib/gnome-shell`.** St/Shell/Gvc/Shew se instalan APARTE de
|
||||
`/usr/lib/girepository-1.0` porque son privados del shell. Es el env que no tiene análogo en KDE.
|
||||
3. **`/var/run` no existía.** `arje-logind-compat` busca el bus en la ruta legacy
|
||||
`/var/run/dbus/system_bus_socket`; sin el symlink reporta `bus_present=false` y se va a modo idle.
|
||||
4. **Política D-Bus de `login1` ausente.** `system.conf` trae `<deny own="*"/>`; normalmente systemd
|
||||
instala el fichero de política del servicio. Acá no hay systemd y el artefacto de
|
||||
arje-logind-compat es sólo el binario ⇒ hubo que escribirla. **Pertenece al artefacto de arje,
|
||||
no a la imagen**; está en el script de imagen sólo para que se vea qué falta empaquetar.
|
||||
5. **`/run/systemd/{sessions,seats,users}` vacíos.** `libelogind` (arje-sdlogin-compat) resuelve la
|
||||
C-ABI sd-login LEYENDO esos ficheros, y nadie los escribe: el `Announce` de arje-logind-compat al
|
||||
bus del fractal falla con `identity mismatch`, así que nunca hay sesión registrada. El
|
||||
`gnome-start` los escribe A MANO — **eso es ANDAMIO, y está marcado como tal en el script**. Con
|
||||
ese puente desaparece el `Failed to setup: Failed to find any matching session`.
|
||||
6. **Y entonces aparece el muro real**: el objeto Session de D-Bus (arriba).
|
||||
|
||||
### Gotcha de diagnóstico que costó una iteración
|
||||
|
||||
`kill -0 <pid>` **tiene éxito sobre un ZOMBI** — el hijo existe como PID hasta que el padre lo
|
||||
cosecha, y el padre es el propio `gnome-start`, que sólo hace `wait` al final. La primera versión del
|
||||
script usaba `kill -0` y reportaba *"sin wayland-0 tras 45s"* cuando en realidad el shell había
|
||||
muerto en el primer segundo. Se mira el `State:` de `/proc/<pid>/status` (vacío o `Z` = muerto), y
|
||||
así sale el código real: **139 = SIGSEGV**.
|
||||
|
||||
## Lo que falta, en orden
|
||||
|
||||
1. **Objeto Session en `arje-logind-compat`** con la propiedad `Seat` (y `TakeControl`/`TakeDevice`
|
||||
sobre él, que es lo que mutter llama enseguida para el DRM master). Esto destraba el arranque.
|
||||
2. **Que escriba `/run/systemd/{sessions,seats,users}`** en vez del puente a mano — y de paso
|
||||
arreglar el `identity mismatch` del Announce.
|
||||
3. **Empaquetar la política D-Bus** dentro del artefacto de arje-logind-compat.
|
||||
4. Recién después: el muro gráfico (GBM/EGL software sobre virtio-gpu), donde KDE ya dejó los fixes
|
||||
pagos y precargados en `gnome-start-qemu.sh` (`GBM_ALWAYS_SOFTWARE`, `kms_swrast`, `LP_NUM_THREADS=1`).
|
||||
Executable
+217
@@ -0,0 +1,217 @@
|
||||
#!/bin/sh
|
||||
# gnome-start-qemu.sh — arranca gnome-shell (Wayland) en una VM QEMU con render por SOFTWARE
|
||||
# (kms_swrast/llvmpipe) sobre el DRM de virtio-gpu. Análogo de plasma-start-qemu.sh, y hereda sus
|
||||
# lecciones caras: el diagnóstico va POR SERIAL porque la ventana gtk se congela si mirada reinicia
|
||||
# su Xwayland (QEMU sigue vivo por dentro). Ver [[kde-metal-qemu-desktop]].
|
||||
#
|
||||
# Se instala como /usr/bin/gnome-start y lo ejecuta DIRECTO el console-getty (no vía login shell).
|
||||
# Salir: matar gnome-shell. Logs: /tmp/gnome-shell.log + tee al serial.
|
||||
#
|
||||
# DIFERENCIA CLAVE CON PLASMA: acá NO hay dos procesos (compositor + shell). gnome-shell ES el
|
||||
# compositor — embebe mutter como librería (libmutter-16.so) y `--wayland` lo hace display server.
|
||||
# Por eso no hay un equivalente de "kwin arriba pero plasmashell murió": si el shell cae, cae todo.
|
||||
set -u
|
||||
# getty ejecuta este script DIRECTO ⇒ PATH viene vacío. Primero eso, antes de invocar nada.
|
||||
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
||||
say() { echo "== gnome-qemu :: $*" | tee -a /dev/console /dev/ttyS0 2>/dev/null; }
|
||||
dump() { echo "---- $1 ----" > /dev/ttyS0 2>/dev/null; cat "$1" > /dev/ttyS0 2>/dev/null; }
|
||||
|
||||
export LD_LIBRARY_PATH=/usr/lib:/lib:/usr/lib/gnome-shell:/usr/lib/mutter-16
|
||||
# GI_TYPELIB_PATH es EL env crítico de esta sesión, y el que no tiene análogo en KDE: gnome-shell es
|
||||
# JavaScript sobre gjs y carga TODO por typelib. Los del sistema van en /usr/lib/girepository-1.0,
|
||||
# pero St/Shell/Gvc/Shew se instalan APARTE en /usr/lib/gnome-shell (son privados del shell). Sin
|
||||
# esta ruta, gjs no encuentra imports.gi.St y el shell muere en el primer require.
|
||||
export GI_TYPELIB_PATH=/usr/lib/girepository-1.0:/usr/lib/gnome-shell:/usr/lib/gnome-shell/girepository-1.0
|
||||
export XDG_DATA_DIRS=/usr/share
|
||||
export XDG_RUNTIME_DIR=/run/user/0
|
||||
export XDG_CONFIG_HOME=/root/.config
|
||||
export XDG_CACHE_HOME=/root/.cache
|
||||
export XDG_SESSION_TYPE=wayland
|
||||
export HOME=/root
|
||||
export GSETTINGS_SCHEMA_DIR=/usr/share/glib-2.0/schemas
|
||||
export LANG=C.UTF-8
|
||||
|
||||
# --- render por software sobre virtio-gpu (los mismos fixes que costaron la campaña KDE) ---
|
||||
export LIBGL_ALWAYS_SOFTWARE=1
|
||||
export GALLIUM_DRIVER=llvmpipe
|
||||
# GBM_ALWAYS_SOFTWARE=1 — virtio-gpu-pci SIN 3D expone un nodo KMS-only. Sin esto gbm carga kms_swrast
|
||||
# pero deja gbm_dri->software=FALSE y dri2_initialize_drm entra al bloque `if(!software)` llamando
|
||||
# queryCompatibleRenderOnlyDeviceFd(), NULL en el driver software ⇒ SIGSEGV. Fue el MURO 2 de KDE.
|
||||
export GBM_ALWAYS_SOFTWARE=1
|
||||
# kms_swrast y NO swrast: el compositor abre card0 como KMS y crea un gbm device encima ⇒ necesita el
|
||||
# driver software PARA KMS (dumb buffers). Con 'swrast' pelado gbm_create_device falla.
|
||||
export MESA_LOADER_DRIVER_OVERRIDE=kms_swrast
|
||||
export LIBGL_DRIVERS_PATH=/usr/lib/dri
|
||||
# llvmpipe SINCRÓNICO: su JIT multi-thread segfaultea al componer la 1ra superficie en esta VM.
|
||||
export LP_NUM_THREADS=1
|
||||
# Análogos mutter de dos fixes de kwin: cursor por software (el plano de cursor por HW no se presenta
|
||||
# en virtio-gpu con render software ⇒ cursor invisible) y modeset simple en vez de atomic.
|
||||
export MUTTER_DEBUG_DISABLE_HW_CURSORS=1
|
||||
export MUTTER_DEBUG_FORCE_KMS_MODE=simple
|
||||
|
||||
if [ "${DIAG:-1}" = 1 ]; then
|
||||
export G_MESSAGES_DEBUG=all
|
||||
export MUTTER_DEBUG=1
|
||||
export EGL_LOG_LEVEL=debug
|
||||
export LIBGL_DEBUG=verbose
|
||||
fi
|
||||
|
||||
mkdir -p "$XDG_RUNTIME_DIR" "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" /run/dbus /var/lib/dbus /root
|
||||
chmod 700 "$XDG_RUNTIME_DIR"
|
||||
[ -s /etc/machine-id ] || cat /proc/sys/kernel/random/uuid 2>/dev/null | tr -d - > /etc/machine-id
|
||||
cp -f /etc/machine-id /var/lib/dbus/machine-id 2>/dev/null || true
|
||||
|
||||
# gschemas.compiled: meson NO lo genera cuando DESTDIR está seteado (lo dice en el log del build:
|
||||
# "Skipping custom install script because DESTDIR is set"). Sin él, GSettings aborta al primer
|
||||
# g_settings_new() y el shell no llega ni a abrir el DRM. Se compila acá, en el primer arranque.
|
||||
if [ ! -f "$GSETTINGS_SCHEMA_DIR/gschemas.compiled" ]; then
|
||||
say "compilando $(ls $GSETTINGS_SCHEMA_DIR/*.xml 2>/dev/null | wc -l) esquemas GSettings..."
|
||||
glib-compile-schemas "$GSETTINGS_SCHEMA_DIR" 2>/tmp/schemas.log \
|
||||
&& say "esquemas OK" || { say "!! glib-compile-schemas FALLÓ:"; dump /tmp/schemas.log; }
|
||||
fi
|
||||
|
||||
say "GPU DRM: $(ls /dev/dri/ 2>/dev/null | tr '\n' ' ')"
|
||||
[ -e /dev/dri/card0 ] || say "!! no hay /dev/dri/card0 — ¿virtio-gpu no cargó?"
|
||||
|
||||
# D-Bus de SISTEMA: mutter habla con logind por el bus de sistema. En esta distro login1 lo provee
|
||||
# arje-logind-compat; si no está, mutter cae a su ruta sin sesión (que es justo lo que libelogind
|
||||
# vino a cubrir del lado de la C-ABI). El bus tiene que existir igual.
|
||||
if [ ! -S /run/dbus/system_bus_socket ]; then
|
||||
if dbus-daemon --system --fork 2>/tmp/dbus-system.log; then
|
||||
say "system bus ON"
|
||||
else
|
||||
say "!! system bus NO arrancó:"; dump /tmp/dbus-system.log
|
||||
fi
|
||||
fi
|
||||
|
||||
# D-Bus de sesión: gnome-shell EXIGE uno (registra org.gnome.Shell y org.gnome.Mutter.*).
|
||||
if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
|
||||
DBUS_SESSION_BUS_ADDRESS=$(dbus-daemon --session --fork --print-address 2>/dev/null)
|
||||
export DBUS_SESSION_BUS_ADDRESS
|
||||
fi
|
||||
say "dbus sesión: ${DBUS_SESSION_BUS_ADDRESS:-NO ARRANCO}"
|
||||
|
||||
# arje-logind-compat: el login1 del fractal. Va DESPUÉS del bus de sistema (registra
|
||||
# org.freedesktop.login1 ahí) y ANTES del shell. Mutter no sólo lo consulta por D-Bus: pregunta por
|
||||
# SU sesión vía la C-ABI sd-login, que libelogind resuelve LEYENDO /run/systemd/sessions. Sin este
|
||||
# daemon esos ficheros no existen y mutter muere en "Failed to find any matching session".
|
||||
if [ -x /usr/bin/arje-logind-compat ]; then
|
||||
/usr/bin/arje-logind-compat >/tmp/logind-compat.log 2>&1 &
|
||||
i=0
|
||||
while [ $i -lt 40 ]; do
|
||||
[ -d /run/systemd/sessions ] && [ -n "$(ls /run/systemd/sessions 2>/dev/null)" ] && break
|
||||
i=$((i+1)); sleep 0.25
|
||||
done
|
||||
say "logind-compat: sesiones=$(ls /run/systemd/sessions 2>/dev/null | tr '\n' ' ') seats=$(ls /run/systemd/seats 2>/dev/null | tr '\n' ' ')"
|
||||
[ -n "$(ls /run/systemd/sessions 2>/dev/null)" ] || dump /tmp/logind-compat.log
|
||||
else
|
||||
say "!! sin /usr/bin/arje-logind-compat — mutter no va a hallar sesión"
|
||||
fi
|
||||
|
||||
# ── PUENTE EXPLÍCITO: el estado de sesión que arje-logind-compat todavía no escribe ─────────────
|
||||
# ESTO ES ANDAMIO, NO LA SOLUCIÓN. arje-logind-compat adquiere org.freedesktop.login1 y sirve el
|
||||
# Manager por D-Bus, pero NO deja nada en /run/systemd/{sessions,seats,users} — su handshake con el
|
||||
# bus de arje falla ("Announce respuesta inesperada: identity mismatch"), así que nunca hay una
|
||||
# sesión registrada por el fractal. Y mutter no pregunta por D-Bus: usa la C-ABI sd-login, que
|
||||
# libelogind (arje-sdlogin-compat) resuelve LEYENDO esos ficheros. De ahí "Failed to find any
|
||||
# matching session" aun con login1 arriba.
|
||||
#
|
||||
# Escribimos el estado a mano, en el formato que libelogind lee (claves sacadas del propio .so:
|
||||
# LEADER/SEAT/CLASS/TYPE/ACTIVE/STATE/VTNR y CAN_GRAPHICAL del seat). Sirve para PROBAR que el resto
|
||||
# de la pila funciona; el arreglo de verdad es que arje-logind-compat registre la sesión del fractal
|
||||
# —o que el Announce deje de fallar— y escriba esto solo. Queda marcado para no confundir andamio
|
||||
# con producto.
|
||||
if [ -z "$(ls /run/systemd/sessions 2>/dev/null)" ]; then
|
||||
say "puente: escribiendo estado de sesión a mano (arje-logind-compat no lo hizo)"
|
||||
mkdir -p /run/systemd/sessions /run/systemd/seats /run/systemd/users
|
||||
cat > /run/systemd/sessions/1 <<EOF
|
||||
UID=0
|
||||
NAME=root
|
||||
LEADER=$$
|
||||
SEAT=seat0
|
||||
TYPE=wayland
|
||||
CLASS=user
|
||||
ACTIVE=1
|
||||
STATE=active
|
||||
VTNR=1
|
||||
REMOTE=0
|
||||
EOF
|
||||
cat > /run/systemd/seats/seat0 <<EOF
|
||||
CAN_GRAPHICAL=1
|
||||
CAN_MULTI_SESSION=1
|
||||
ACTIVE=1
|
||||
ACTIVE_SESSIONS=1
|
||||
SESSIONS=1
|
||||
EOF
|
||||
cat > /run/systemd/users/0 <<EOF
|
||||
NAME=root
|
||||
STATE=active
|
||||
SESSIONS=1
|
||||
ACTIVE_SESSIONS=1
|
||||
EOF
|
||||
fi
|
||||
# XDG_SESSION_ID/SEAT/VTNR: libelogind los menciona entre sus símbolos, y son además el contrato
|
||||
# estándar por el que un proceso sabe cuál es SU sesión sin consultar por PID.
|
||||
export XDG_SESSION_ID=1
|
||||
export XDG_SEAT=seat0
|
||||
export XDG_VTNR=1
|
||||
|
||||
if [ "${DIAG:-1}" = 1 ]; then
|
||||
ulimit -c unlimited 2>/dev/null || true
|
||||
echo '/core.%e.%p' > /proc/sys/kernel/core_pattern 2>/dev/null || true
|
||||
fi
|
||||
|
||||
say "typelibs visibles: $(ls /usr/lib/girepository-1.0/*.typelib 2>/dev/null | wc -l) sistema + $(ls /usr/lib/gnome-shell/*.typelib 2>/dev/null | wc -l) del shell"
|
||||
say "lanzando gnome-shell --wayland (DRM virtio, software GL)..."
|
||||
/usr/bin/gnome-shell --wayland >/tmp/gnome-shell.log 2>&1 &
|
||||
SHELL_PID=$!
|
||||
# stream EN VIVO al serial (busybox sed no soporta -u ⇒ tail -f directo, sin pipes).
|
||||
( echo "==== gnome-shell.log (stream vivo) ===="; tail -f /tmp/gnome-shell.log ) > /dev/ttyS0 2>/dev/null &
|
||||
|
||||
# OJO con el test de vivo: `kill -0` TIENE ÉXITO sobre un ZOMBI (el hijo existe como PID hasta que
|
||||
# el padre lo cosecha, y acá el padre es este script, que sólo hace wait al final). La primera
|
||||
# versión usaba kill -0 y por eso reportó "sin wayland-0 tras 45s" cuando en realidad gnome-shell ya
|
||||
# había muerto en el primer segundo: el diagnóstico decía "0 hilos" porque no quedaba proceso, sólo
|
||||
# la entrada zombi. Se mira el State de /proc en su lugar.
|
||||
alive() { s=$(sed -n 's/^State:[[:space:]]*//p' /proc/$1/status 2>/dev/null); case "$s" in ""|Z*) return 1;; *) return 0;; esac; }
|
||||
i=0
|
||||
while [ $i -lt 90 ]; do
|
||||
[ -S "$XDG_RUNTIME_DIR/wayland-0" ] && break
|
||||
alive $SHELL_PID || { wait $SHELL_PID; RC=$?; say "!! gnome-shell murió (código $RC) antes de exponer wayland-0:"; dump /tmp/gnome-shell.log; say "cores: $(ls /core.* 2>/dev/null | tr '\n' ' ' || echo NINGUNO)"; exec /bin/sh; }
|
||||
i=$((i+1)); sleep 0.5
|
||||
done
|
||||
if [ -S "$XDG_RUNTIME_DIR/wayland-0" ]; then
|
||||
say "compositor OK (wayland-0) — el shell ES el display server"
|
||||
else
|
||||
say "!! sin wayland-0 tras 45s:"; dump /tmp/gnome-shell.log
|
||||
# El shell NO murió: quedó COLGADO. Sin gdb en la imagen, el diagnóstico barato es dónde está
|
||||
# bloqueado el hilo principal (wchan) y contra qué socket, que es lo que distingue "esperando una
|
||||
# respuesta D-Bus que no llega" de "spinning en el renderer".
|
||||
say "--- diagnóstico del cuelgue (pid $SHELL_PID) ---"
|
||||
say "wchan: $(cat /proc/$SHELL_PID/wchan 2>/dev/null)"
|
||||
say "state: $(grep -a ^State /proc/$SHELL_PID/status 2>/dev/null)"
|
||||
say "stack principal:"; cat /proc/$SHELL_PID/stack > /dev/ttyS0 2>/dev/null
|
||||
say "syscall: $(cat /proc/$SHELL_PID/syscall 2>/dev/null)"
|
||||
say "fds abiertos:"; ls -l /proc/$SHELL_PID/fd 2>/dev/null > /dev/ttyS0
|
||||
say "hilos: $(ls /proc/$SHELL_PID/task 2>/dev/null | wc -l)"
|
||||
for t in $(ls /proc/$SHELL_PID/task 2>/dev/null); do
|
||||
say " tid $t wchan=$(cat /proc/$SHELL_PID/task/$t/wchan 2>/dev/null) comm=$(cat /proc/$SHELL_PID/task/$t/comm 2>/dev/null)"
|
||||
done
|
||||
exec /bin/sh
|
||||
fi
|
||||
export WAYLAND_DISPLAY=wayland-0
|
||||
|
||||
( n=0; while sleep 15; do
|
||||
n=$((n+1))
|
||||
say "STATUS +$((n*15))s: gnome-shell=$(pgrep gnome-shell 2>/dev/null | wc -l)"
|
||||
done ) &
|
||||
|
||||
wait $SHELL_PID
|
||||
RC=$?
|
||||
say "gnome-shell salió (código $RC) — volcando log:"
|
||||
dump /tmp/gnome-shell.log
|
||||
cp /tmp/gnome-shell.log /gnome-shell.log 2>/dev/null || true
|
||||
say "cores en /: $(ls -la /core.* 2>/dev/null | tr '\n' ' ' || echo NINGUNO)"
|
||||
sync; sync
|
||||
say "== disco SYNCED. serial CONGELADO. matá QEMU desde el host. =="
|
||||
sleep 3600
|
||||
Executable
+172
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env bash
|
||||
# qemu-desktop-image.sh — imagen de disco EFI GNOME para QEMU (virtio-gpu + software GL), auto-lanza
|
||||
# gnome-shell. Hermano de scripts/kde/qemu-desktop-image.sh; comparte con él la base metal, la
|
||||
# inyección de mesa/LLVM/musl y el truco del console-getty, y sólo diverge en lo que GNOME necesita.
|
||||
#
|
||||
# work/metal-rootfs base: arje-zero PID1 + busybox + kernel(EFI-stub) + firmware
|
||||
# work/gnome-rootfs cierre de gnome-shell (lo arma scripts/gnome/hydrate-gnome.sh, 108/108)
|
||||
# + store/*-mesa-llvmpipe GBM/EGL de software (el rootfs GNOME sólo trae iris_dri, que es Intel real)
|
||||
# + store/*-llvm18 libLLVM que llvmpipe JIT-linkea
|
||||
# + store/*-dejavu-fonts el rootfs GNOME NO trae NINGUNA fuente
|
||||
# → work/gnome-qemu-rootfs (hardlinks) → work/hammer-gnome-qemu.img
|
||||
#
|
||||
# Uso: scripts/gnome/qemu-desktop-image.sh
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
|
||||
|
||||
BASE="${BASE:-work/metal-rootfs}"
|
||||
GNOME="${GNOME:-work/gnome-rootfs}"
|
||||
MESA="${MESA:-$(ls -dt store/*-mesa-llvmpipe 2>/dev/null | head -1)}"
|
||||
LLVM="${LLVM:-$(ls -dt store/*-llvm18 2>/dev/null | head -1)}"
|
||||
FONTS="${FONTS:-$(ls -dt store/*-dejavu-fonts 2>/dev/null | head -1)}"
|
||||
MERGED="${MERGED:-work/gnome-qemu-rootfs}"
|
||||
IMG="${IMG:-work/hammer-gnome-qemu.img}"
|
||||
|
||||
[ -d "$BASE" ] || { echo "falta $BASE" >&2; exit 1; }
|
||||
[ -d "$GNOME" ] || { echo "falta $GNOME — corré scripts/gnome/hydrate-gnome.sh" >&2; exit 1; }
|
||||
[ -n "$MESA" ] && [ -d "$MESA" ] || { echo "falta mesa-llvmpipe en store/" >&2; exit 1; }
|
||||
|
||||
echo "==> fundiendo rootfs por hardlinks (base metal + cierre GNOME)"
|
||||
rm -rf "$MERGED"
|
||||
cp -al "$BASE" "$MERGED"
|
||||
cp -al --remove-destination "$GNOME"/. "$MERGED"/
|
||||
|
||||
echo "==> parcheando mesa software desde $MESA"
|
||||
# El cierre GNOME trae mesa con SÓLO iris_dri.so (Intel real). En QEMU no hay GPU: hace falta
|
||||
# kms_swrast. Se copian los .so de mesa-llvmpipe ENCIMA (--remove-destination rompe el hardlink).
|
||||
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/ (set LLVM=)" >&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 "==> inyectando el runtime C++ de Alpine (deuda CONOCIDA, no accidente)"
|
||||
# libLLVM y mesa-llvmpipe se construyen con gcc ⇒ NEEDean libstdc++/libgcc_s. Y además —esto es
|
||||
# propio de GNOME y quedó documentado al auditar el cierre— **libmozjs-128.so también los NEEDea**,
|
||||
# o sea que la cadena gnome-shell → libgjs → libmozjs arrastra el runtime C++ de Alpine. Es la
|
||||
# "última milla" de matar-gcc que se cerró para cmake con -static-libstdc++ y que spidermonkey no
|
||||
# cubrió. Mientras eso no se re-selle (radio medido: 2), la imagen los inyecta o el shell no carga.
|
||||
for lib in libgcc_s.so.1 libstdc++.so.6; do
|
||||
src=".dev-fs/alpine/usr/lib/$lib"
|
||||
if [ -r "$src" ]; then cp -a --remove-destination "$src"* "$MERGED"/usr/lib/ 2>/dev/null || true
|
||||
else echo " ⚠ sin $lib ($src)"; fi
|
||||
done
|
||||
|
||||
echo "==> inyectando musl — y los DOS sonames"
|
||||
# La base metal es 100% estática (arje-zero+busybox) y el cierre GNOME no trae libc. En musl el
|
||||
# loader ES libc (mismo fichero). Van dos nombres porque conviven dos toolchains: zig-cc emite
|
||||
# NEEDED `libc.so` (359 consumidores) y el gcc/clang de Alpine emite `libc.musl-x86_64.so.1`
|
||||
# (17: todo nss + spidermonkey). Es la MISMA libc; sin el segundo symlink esos 17 no cargan.
|
||||
MUSL="${MUSL:-/usr/lib/musl/lib/libc.so}"
|
||||
[ -r "$MUSL" ] || { echo "no encuentro musl libc ($MUSL) — set 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"
|
||||
ln -sf /usr/lib/musl/lib/libc.so "$MERGED/usr/lib/libc.musl-x86_64.so.1"
|
||||
echo " ✓ libc.so + libc.musl-x86_64.so.1"
|
||||
|
||||
if [ -n "$FONTS" ] && [ -d "$FONTS" ]; then
|
||||
echo "==> inyectando fuentes ($(basename "$FONTS"))"
|
||||
cp -a --remove-destination "$FONTS"/usr/share/fonts/. "$MERGED"/usr/share/fonts/ 2>/dev/null || \
|
||||
{ mkdir -p "$MERGED"/usr/share/fonts && cp -a "$FONTS"/usr/share/fonts/. "$MERGED"/usr/share/fonts/; }
|
||||
else
|
||||
echo " ⚠ sin dejavu-fonts en el store: el escritorio saldrá con tofu"
|
||||
fi
|
||||
|
||||
echo "==> inyectando arje-logind-compat (el login1 del fractal)"
|
||||
# EL ESLABÓN QUE FALTABA, descubierto arrancando la imagen: mutter levanta como display server y
|
||||
# muere en `Failed to setup: Failed to find any matching session`. Ese chequeo es de logind — mutter
|
||||
# pregunta por su sesión vía la C-ABI sd-login, que acá provee libelogind (arje-sdlogin-compat)
|
||||
# LEYENDO /run/systemd/{sessions,users,seats}. Pero alguien tiene que ESCRIBIR esos ficheros, y ese
|
||||
# alguien es arje-logind-compat, el daemon D-Bus que se hace pasar por org.freedesktop.login1.
|
||||
# libelogind entra solo (es dep de mutter); el daemon no, porque nadie lo declara como dep de build.
|
||||
ALC="${ALC:-$(ls -dt store/*-arje-logind-compat 2>/dev/null | head -1)}"
|
||||
if [ -n "$ALC" ] && [ -d "$ALC" ]; then
|
||||
install -Dm755 "$ALC"/usr/bin/arje-logind-compat "$MERGED"/usr/bin/arje-logind-compat
|
||||
echo " ✓ $(basename "$ALC" | cut -c1-12)"
|
||||
else
|
||||
echo " ✗ falta arje-logind-compat en store/ — mutter no hallará sesión" >&2; exit 1
|
||||
fi
|
||||
|
||||
echo "==> política D-Bus para org.freedesktop.login1"
|
||||
# Segundo muro del mismo eslabón: con el bus ya visible, dbus RECHAZA que el daemon se adueñe del
|
||||
# nombre — "not allowed to own the service org.freedesktop.login1 due to security policies". El
|
||||
# system.conf de dbus trae `<deny own="*"/>` por defecto: cada servicio de sistema aporta su propio
|
||||
# fichero de política, y systemd normalmente instala el de login1. Acá no hay systemd, y el artefacto
|
||||
# de arje-logind-compat es sólo el binario ⇒ nadie pone la política. La escribimos: root puede
|
||||
# adueñarse del nombre, y cualquiera puede hablarle (mutter consulta login1 sin ser root).
|
||||
# TODO: esto pertenece al artefacto de arje-logind-compat, no a la imagen; acá está de forma
|
||||
# explícita para que se vea qué falta empaquetar del lado de arje.
|
||||
install -d "$MERGED/usr/share/dbus-1/system.d"
|
||||
cat > "$MERGED/usr/share/dbus-1/system.d/org.freedesktop.login1.conf" <<'XML'
|
||||
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
<policy user="root">
|
||||
<allow own="org.freedesktop.login1"/>
|
||||
</policy>
|
||||
<policy context="default">
|
||||
<allow send_destination="org.freedesktop.login1"/>
|
||||
<allow receive_sender="org.freedesktop.login1"/>
|
||||
</policy>
|
||||
</busconfig>
|
||||
XML
|
||||
chmod 644 "$MERGED/usr/share/dbus-1/system.d/org.freedesktop.login1.conf"
|
||||
echo " ✓ org.freedesktop.login1.conf"
|
||||
|
||||
echo "==> usuario messagebus (el system bus hace setuid a él)"
|
||||
for f in passwd:messagebus:x:81:81:messagebus:/dev/null:/sbin/nologin group:messagebus:x:81:; do
|
||||
file="${f%%:*}"; line="${f#*:}"
|
||||
if ! grep -q '^messagebus:' "$MERGED/etc/$file" 2>/dev/null; then
|
||||
tmp="$MERGED/etc/$file.new"
|
||||
{ cat "$MERGED/etc/$file"; echo "$line"; } > "$tmp"
|
||||
chmod 644 "$tmp"; mv "$tmp" "$MERGED/etc/$file"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "==> instalando /usr/bin/gnome-start"
|
||||
install -Dm755 scripts/gnome/gnome-start-qemu.sh "$MERGED/usr/bin/gnome-start"
|
||||
|
||||
echo "==> auto-lanzar GNOME: console-getty ejecuta gnome-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/gnome-start" if a == "/bin/sh" else a) for a in argv]
|
||||
print(" console-getty argv →", argv)
|
||||
tmp = p + ".new"
|
||||
with open(tmp, "w") as f:
|
||||
json.dump(c, f, indent=2)
|
||||
os.chmod(tmp, 0o644)
|
||||
os.replace(tmp, p)
|
||||
PY
|
||||
|
||||
# /var/run → /run: arje-logind-compat busca el bus de sistema en la ruta LEGACY
|
||||
# (/var/run/dbus/system_bus_socket) y dbus-daemon escucha en /run/dbus/system_bus_socket. En casi
|
||||
# toda distro /var/run es un symlink a /run desde hace 15 años; esta base metal es tan mínima que ni
|
||||
# siquiera tiene /var/run. Sin el enlace el daemon reporta `bus_present=false` y se va a "modo idle",
|
||||
# o sea que nunca registra login1 ni escribe /run/systemd/sessions ⇒ mutter no halla sesión.
|
||||
ln -sfn /run "$MERGED/var/run"
|
||||
|
||||
mkdir -p "$MERGED/root/.config" "$MERGED/root/.cache" "$MERGED/run/user/0" "$MERGED/tmp"
|
||||
chmod 1777 "$MERGED/tmp"
|
||||
|
||||
echo "==> rootfs fundido: $(du -sh "$MERGED" | cut -f1)"
|
||||
need_mb=$(du -sm "$MERGED" | cut -f1)
|
||||
ROOT_SIZE="${ROOT_SIZE:-$(( need_mb + 1536 ))}"
|
||||
echo "==> ROOT_SIZE=${ROOT_SIZE} MiB"
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user