diff --git a/docs/evidencia/gnome-shell-qemu-2026-08-03-pid1-sellado.png b/docs/evidencia/gnome-shell-qemu-2026-08-03-pid1-sellado.png new file mode 100644 index 00000000..71b03f21 Binary files /dev/null and b/docs/evidencia/gnome-shell-qemu-2026-08-03-pid1-sellado.png differ diff --git a/docs/runbooks/gnome-qemu-desktop.md b/docs/runbooks/gnome-qemu-desktop.md index 932bbfdc..8941ae6e 100644 --- a/docs/runbooks/gnome-qemu-desktop.md +++ b/docs/runbooks/gnome-qemu-desktop.md @@ -404,8 +404,43 @@ elige `/dev/dri/card0` como primaria sin quejarse. Es el rédito directo de la c (ver el comentario de la receta: por qué NO se cherry-pickeó, con la medición). **La lección general: antes de arreglar un WARN, buscá quién lo emite y si ya está arreglado upstream.** Nuestro pin estaba 3951 commits detrás y nadie lo había mirado. -4. Con la capa JS viva: validar CON PANTALLA (`DISP=gtk`), nunca sólo por serial — regla heredada de - KDE ([[metal-dual-desktop-en-curso]]). +4. ✅ **HECHO (2026-08-03)** — validado CON PANTALLA de verdad (`DISP=gtk`, ventana en el laptop) y no + sólo por `screendump`: el Overview pinta con el PID1 sellado nuevo, **689 colores distintos**, azul + GNOME `(2,60,136)` y grises del shell `(34,34,38)` al frente, cursor visible. + Evidencia: [`gnome-shell-qemu-2026-08-03-pid1-sellado.png`](../evidencia/gnome-shell-qemu-2026-08-03-pid1-sellado.png). + **Y aplicar la regla pagó otra vez**: el arranque destapó una regresión que el serial daba por + verde — ver abajo. + +### La carrera de PolicyKit1: el resumen final decía OK y los clientes ya estaban muertos + +Arrancando para la validación con pantalla, `accounts-daemon` y `upowerd` **murieron los dos**: + +``` +Error calling StartServiceByName for org.freedesktop.PolicyKit1: + Failed to execute program org.freedesktop.PolicyKit1: Permission denied +Failed to initialize daemon +``` + +El comentario del script ya decía que arje-polkit-compat «VA ANTES que accounts-daemon y upowerd», y +**iba antes** — en el orden de las líneas. Pero se lanza en background y tarda ~2 s en adquirir +`org.freedesktop.PolicyKit1`, mientras los otros dos arrancan de inmediato y llaman a +`polkit_authority_get_sync()` dentro de ese hueco. Sin dueño del nombre, D-Bus intenta ACTIVAR el +servicio, la activación falla, y los dos daemons se mueren. + +**Lo peligroso no fue el fallo sino el informe.** Los tres `esperar_nombre` estaban todos al final: +para cuando corrían, el shim ya tenía el nombre y el resumen imprimía `bus: org.freedesktop.PolicyKit1 +OK` sobre dos clientes que hacía rato eran cadáveres. Un chequeo que corre después de la carrera no la +mide. + +El arreglo es mover la espera al PRODUCTOR: `esperar_nombre` se define antes del primer daemon, y el +bloque de polkit espera su propio nombre **antes** de que se lance ningún cliente suyo. Es la misma +lección de «un pid no es un servicio» aplicada del otro lado: *lanzar en orden no es estar listo en +orden*. Después del fix: `PolicyKit1 OK` → `accounts-daemon lanzado` → `upowerd sigue vivo` → +`Accounts OK` → `UPower OK`. + +Dato suelto que dejó el mismo arranque: **colord ahora MUERE** («colord MURIÓ al arrancar») en vez de +quedarse vivo sin adquirir el nombre. Su log sigue cortándose después de abrir las tres bases de datos. +Cambió el síntoma, no la causa conocida; el ítem sigue aparcado. ## Recetas nuevas de esta etapa diff --git a/scripts/gnome/gnome-start-qemu.sh b/scripts/gnome/gnome-start-qemu.sh index f0639914..bc4de733 100755 --- a/scripts/gnome/gnome-start-qemu.sh +++ b/scripts/gnome/gnome-start-qemu.sh @@ -214,14 +214,59 @@ export XDG_SESSION_ID=1 export XDG_SEAT=seat0 export XDG_VTNR=1 +# ── ESPERAR EL NOMBRE, NO EL PROCESO ──────────────────────────────────────────────────────────── +# El paso que faltaba, y es la otra mitad de «un pid no es un servicio»: comprobar que el daemon +# ADQUIRIÓ su nombre en el bus. Se le da tiempo y, si no aparece, se vuelca su log y la lista de +# nombres del bus — que es el dato que dice si el problema es «no arrancó», «arrancó y no pidió el +# nombre» o «lo pidió y se lo negaron». +# $3 = décimas de segundo a esperar (default 20 = 10 s). colord necesita más: en el PRIMER arranque +# crea sus tres bases de datos en /var/lib/colord y escanea los directorios de perfiles ICC antes de +# pedir el nombre, y con 10 s daba «NO apareció» sin estar colgado — o sea un falso negativo. +# +# Se define ACÁ ARRIBA —antes del primer daemon— y no al final junto a las llamadas, porque el bloque +# de polkit de abajo la necesita para poder ESPERAR antes de lanzar a sus clientes. +esperar_nombre() { + n="$1"; log="$2"; lim="${3:-20}"; i=0 + while [ $i -lt "$lim" ]; do + if dbus-send --system --dest=org.freedesktop.DBus --print-reply \ + /org/freedesktop/DBus org.freedesktop.DBus.NameHasOwner "string:$n" 2>/dev/null \ + | grep -q "boolean true"; then + say "bus: $n OK" + return 0 + fi + i=$((i+1)); sleep 0.5 + done + say "!! bus: $n NO apareció en $((lim/2))s" + [ -n "$log" ] && [ -s "$log" ] && dump "$log" + say "nombres en el bus de sistema:" + dbus-send --system --dest=org.freedesktop.DBus --print-reply \ + /org/freedesktop/DBus org.freedesktop.DBus.ListNames 2>/dev/null \ + | tr -d '" ' | grep -v '^\s*$' > /tmp/busnames.log + dump /tmp/busnames.log + return 1 +} + # arje-polkit-compat: el PolicyKit1 del fractal. **VA ANTES que accounts-daemon y upowerd**, y el # orden no es estético: los dos se bloquean en `polkit_authority_get_sync()` al arrancar, así que si # `org.freedesktop.PolicyKit1` no está en el bus se quedan conectados pero SIN adquirir su nombre —y # gnome-shell después espera 25 s por cada uno—. Nuestra receta `polkit` es libs-only a propósito # (el demonio lo pone arje). Ojo: el shim autoriza TODO; ver recipes/arje-polkit-compat.toml. +# +# ⚠ Y «antes» tiene que ser POR EL NOMBRE, no por el orden de las líneas. Lanzarlo primero no alcanza: +# se lanza en background y tarda ~2 s en adquirir `org.freedesktop.PolicyKit1`, mientras accounts-daemon +# y upowerd arrancan de inmediato y llaman a `polkit_authority_get_sync()` en ese hueco. Entonces D-Bus +# intenta ACTIVAR el servicio (nadie tiene el nombre todavía) y falla: +# Error calling StartServiceByName for org.freedesktop.PolicyKit1: +# Failed to execute program org.freedesktop.PolicyKit1: Permission denied +# y los dos daemons se MUEREN («Failed to initialize daemon»). Después el shim adquiere el nombre +# tranquilo y el chequeo del final dice `PolicyKit1 OK` — o sea que el resumen final da verde y los +# clientes ya están muertos. La carrera es real y la ganaba cualquiera: es la misma lección de «un pid +# no es un servicio», aplicada al productor en vez de al consumidor. if [ -x /usr/bin/arje-polkit-compat ]; then /usr/bin/arje-polkit-compat >/tmp/polkit-compat.log 2>&1 & say "arje-polkit-compat lanzado (pid $!)" + esperar_nombre org.freedesktop.PolicyKit1 /tmp/polkit-compat.log \ + || say "!! sigo igual: accounts-daemon y upowerd van a fallar al pedir la autoridad" else say "!! sin arje-polkit-compat — accounts-daemon y upowerd se van a colgar pidiendo autorización" fi @@ -266,34 +311,6 @@ else say "!! sin upowerd — el shell va a esperar 25s a su activación D-Bus y seguir sin batería" fi -# ── ESPERAR EL NOMBRE, NO EL PROCESO ──────────────────────────────────────────────────────────── -# El paso que faltaba, y es la otra mitad de «un pid no es un servicio»: comprobar que el daemon -# ADQUIRIÓ su nombre en el bus. Se le da tiempo y, si no aparece, se vuelca su log y la lista de -# nombres del bus — que es el dato que dice si el problema es «no arrancó», «arrancó y no pidió el -# nombre» o «lo pidió y se lo negaron». -# $3 = décimas de segundo a esperar (default 20 = 10 s). colord necesita más: en el PRIMER arranque -# crea sus tres bases de datos en /var/lib/colord y escanea los directorios de perfiles ICC antes de -# pedir el nombre, y con 10 s daba «NO apareció» sin estar colgado — o sea un falso negativo. -esperar_nombre() { - n="$1"; log="$2"; lim="${3:-20}"; i=0 - while [ $i -lt "$lim" ]; do - if dbus-send --system --dest=org.freedesktop.DBus --print-reply \ - /org/freedesktop/DBus org.freedesktop.DBus.NameHasOwner "string:$n" 2>/dev/null \ - | grep -q "boolean true"; then - say "bus: $n OK" - return 0 - fi - i=$((i+1)); sleep 0.5 - done - say "!! bus: $n NO apareció en $((lim/2))s" - [ -n "$log" ] && [ -s "$log" ] && dump "$log" - say "nombres en el bus de sistema:" - dbus-send --system --dest=org.freedesktop.DBus --print-reply \ - /org/freedesktop/DBus org.freedesktop.DBus.ListNames 2>/dev/null \ - | tr -d '" ' | grep -v '^\s*$' > /tmp/busnames.log - dump /tmp/busnames.log - return 1 -} # ── AUDIO: PipeWire (decisión del usuario, 2026-07-29) ────────────────────────────────────────── # Van TRES procesos y el orden importa, porque cada uno es cliente del anterior: # 1. `pipewire` — el servidor. Crea $XDG_RUNTIME_DIR/pipewire-0. @@ -394,7 +411,8 @@ else fi esperar_nombre org.freedesktop.login1 /tmp/logind-compat.log -esperar_nombre org.freedesktop.PolicyKit1 /tmp/polkit-compat.log +# PolicyKit1 NO se chequea acá: ya se esperó arriba, antes de lanzar a sus clientes. Chequearlo sólo +# al final era precisamente lo que dejaba pasar la carrera con verde. esperar_nombre org.freedesktop.Accounts /tmp/accounts-daemon.log esperar_nombre org.freedesktop.UPower /tmp/upowerd.log esperar_nombre org.freedesktop.ColorManager /tmp/colord.log 80 # 40 s: ver el comentario