🖥 cosmic/metal: la imagen forzaba render por SOFTWARE — el viaje no podía ganar
Diagnóstico del 1er viaje al OptiPlex 3060 (Coffee Lake, UHD 630). El dmesg del propio USB muestra que la GPU estaba PERFECTA: [drm] Found coffeelake (device ID 3e92) ... Initialized i915 1.6.0 on minor 0 fbcon: i915drmfb (fb0) is primary device Lo que fallaba era el script de arranque. `cosmic-start-qemu.sh` exportaba LIBGL_ALWAYS_SOFTWARE=1 y MESA_LOADER_DRIVER_OVERRIDE=kms_swrast INCONDICIONAL — correcto sobre virtio-gpu, letal sobre Intel real: la mesa del corpus va con -Dgallium-drivers=iris -Dllvm=disabled y NO EXISTE ningún kms_swrast_dri.so ⇒ EGL falla. Y en el mejor caso habría sido peor: si arrancaba, componía por software y ScreenCast fallaba igual que en QEMU ⇒ el viaje NO PODÍA contestar su pregunta. El origen es un comentario que yo escribí en metal-desktop-image.sh afirmando que el script no tenía supuestos del emulador. Los tenía, y el nombre del fichero lo decía. Ahora: - `cosmic-start-qemu.sh` → `cosmic-start.sh` (se llama por lo que hace). - El modo de GL se DETECTA con dos patas: driver DRM del kernel Y .so de mesa presente. Si no hay ninguna, lo DICE en vez de morir dentro de EGL. - La imagen VERIFICA que el script instalado no fuerce kms_swrast (falla el build si vuelve a pasar). Y correr cosmic-start sobre la imagen de metal POR PRIMERA VEZ (antes sólo se validaba que llegara al prompt) destapó que le faltaba entera la preparación de sesión que sólo tenía qemu-desktop-image.sh: sin grupo `video` no arranca seatd, sin usuario `messagebus` no arranca el bus de SISTEMA — y sin bus de sistema NO HAY PORTAL, o sea que portal-probe screencast no habría tenido con quién hablar aunque el GL fuese perfecto. Copiada: arje-logind-compat + política, video, messagebus, setuid del launch-helper, /var/run→/run, COSMIC_MODE, libc.musl-x86_64.so.1. Además, para que el próximo fallo en metal no cueste otro viaje a ciegas: - Los logs van a /var/log/cosmic (ext4) y no a /tmp (RAM, se evapora al apagar). `dump()` también, que iba SÓLO al serial — donde se perdió la explicación de los tres fallos de arriba. - authorized_keys horneada: sshd escuchaba en :22 pero era INALCANZABLE (PasswordAuthentication no, sin clave) ⇒ ahora se depura por red. - netup-wait: el r8169 levanta el enlace a los 20,2s y el ente sshd pedía DHCP a los 13,7s, sin reintento. En QEMU no se ve: virtio-net tiene carrier desde el instante cero. - firmware i915 de otras generaciones (la base sólo traía tgl_*; el Coffee Lake pedía kbl_dmc_ver1_04.bin). Validado arrancando como usb-storage: seatd OK, bus de sistema OK, login1 OK, pipewire+wireplumber OK, compositor OK. La línea de GL dice la verdad en QEMU. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,18 @@
|
||||
#!/bin/sh
|
||||
# cosmic-start — lo que el console-getty ejecuta en vez de /bin/sh en la imagen COSMIC de QEMU.
|
||||
# Hermano de gnome-start y plasma-start; hereda de ellos todo lo que ya está pago y sólo diverge en
|
||||
# lo que COSMIC necesita.
|
||||
# cosmic-start — levanta la sesión COSMIC. Lo corre el getty (o el usuario a mano), y es EL MISMO
|
||||
# fichero en la imagen de QEMU y en la de metal. Hermano de gnome-start y plasma-start; hereda de
|
||||
# ellos todo lo que ya está pago y sólo diverge en lo que COSMIC necesita.
|
||||
#
|
||||
# ⚠ SE LLAMABA `cosmic-start-qemu.sh` Y ESO COSTÓ UN VIAJE FÍSICO (2026-08-05). El nombre decía la
|
||||
# verdad —forzaba render por software con `LIBGL_ALWAYS_SOFTWARE` y `MESA_LOADER_DRIVER_OVERRIDE=
|
||||
# kms_swrast`, que es lo correcto sobre virtio-gpu— pero la imagen de METAL lo instalaba igual, con
|
||||
# un comentario que afirmaba que «nada de lo que hace es específico del emulador». En un Coffee Lake
|
||||
# real la mesa del corpus es `iris` y NO EXISTE ningún `kms_swrast_dri.so`: el override no degrada a
|
||||
# software, hace fallar EGL y el compositor no arranca. Y en el mejor caso habría sido peor: si
|
||||
# hubiera arrancado, habría compuesto por software y ScreenCast habría fallado por el mismo motivo
|
||||
# que en QEMU ⇒ **el viaje no podía contestar la pregunta que lo motivaba**.
|
||||
# Por eso ahora el modo de GL se DETECTA (ver más abajo) en vez de hardcodearse, y el fichero se
|
||||
# llama por lo que hace y no por dónde lo probamos.
|
||||
#
|
||||
# Es `/bin/sh` (busybox ash) a propósito, aunque `start-cosmic` de upstream sea bash: este script
|
||||
# corre ANTES, prepara el entorno, y después le pasa la posta. Cuanto menos dependa, más temprano
|
||||
@@ -13,8 +24,20 @@ set -u
|
||||
# la preparación fallaban con «mkdir: not found» y la sesión arrancaba IGUAL, porque los directorios
|
||||
# venían horneados en la imagen. Un fallo que no se cobra nada hasta que alguien cambia la imagen.
|
||||
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
||||
say() { echo "== cosmic-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; }
|
||||
|
||||
# ── DÓNDE VAN LOS LOGS: al DISCO, no a /tmp ─────────────────────────────────────────────────────
|
||||
# /tmp es tmpfs, o sea RAM. En QEMU da igual porque el serial queda en el terminal del que lanzó la
|
||||
# VM; en METAL, si la sesión falla, la evidencia se evapora al apagar y el viaje físico se gasta sin
|
||||
# aprender nada — pasó exactamente así. /var/log/cosmic vive en la ext4 de la imagen ⇒ sobrevive al
|
||||
# apagón y se puede leer desde otra máquina sin bootear, con `debugfs -R "dump ..." /dev/sdX2`.
|
||||
LOG=/var/log/cosmic
|
||||
mkdir -p "$LOG" 2>/dev/null
|
||||
[ -d "$LOG" ] && [ -w "$LOG" ] || LOG=/tmp
|
||||
|
||||
say() { echo "== cosmic :: $*" | tee -a /dev/console /dev/ttyS0 "$LOG/cosmic-start.log" 2>/dev/null; }
|
||||
# `dump` iba SÓLO al serial, y ahí se perdió la explicación de los tres fallos de la primera corrida
|
||||
# en la imagen de metal: sin puerto serie, los volcados se escriben a la nada. Ahora también a disco.
|
||||
dump() { { echo "---- $1 ----"; cat "$1" 2>/dev/null; } | tee -a /dev/ttyS0 "$LOG/cosmic-start.log" >/dev/null 2>&1; }
|
||||
alive() { s=$(sed -n 's/^State:[[:space:]]*//p' /proc/$1/status 2>/dev/null); case "$s" in ""|Z*) return 1;; *) return 0;; esac; }
|
||||
|
||||
say "arrancando (pid $$)"
|
||||
@@ -50,16 +73,41 @@ export HOME=/root SHELL=/bin/bash # PATH ya salió arriba, antes del primer mk
|
||||
|
||||
say "kernel $(uname -r) · drm: $(ls /dev/dri 2>/dev/null | tr '\n' ' ')"
|
||||
|
||||
# ── Render por software: la VM no tiene GPU ─────────────────────────────────────────────────────
|
||||
# Las cuatro perillas vienen pagas de las campañas KDE y GNOME. `GBM_ALWAYS_SOFTWARE` + el driver
|
||||
# `kms_swrast` son lo que hace que mesa acepte crear un device GBM sobre virtio-gpu sin acelerador;
|
||||
# `LP_NUM_THREADS=1` evita que llvmpipe se coma los cuatro vcpus y deje al compositor sin planificar.
|
||||
export GBM_ALWAYS_SOFTWARE=1
|
||||
export LIBGL_ALWAYS_SOFTWARE=1
|
||||
export MESA_LOADER_DRIVER_OVERRIDE=kms_swrast
|
||||
export LP_NUM_THREADS=1
|
||||
# ── GL: HARDWARE si lo hay, software si no — DETECTADO, nunca hardcodeado ───────────────────────
|
||||
# La regla tiene DOS patas y hay que cumplir las dos: que el KERNEL exponga un driver DRM real
|
||||
# (i915/amdgpu/radeon/nouveau, no virtio_gpu ni vgem), y que MESA traiga el `.so` correspondiente.
|
||||
# Con una sola no alcanza, y por eso el hardcodeo anterior era una trampa: la mesa del corpus va con
|
||||
# `-Dgallium-drivers=iris -Dllvm=disabled`, así que sobre un AMD real habría kernel pero no userland,
|
||||
# y en QEMU hay virtio-gpu pero el camino bueno es swrast. Si no se cumple NINGUNA, se dice fuerte en
|
||||
# vez de morir adentro de EGL con un mensaje que no señala la causa.
|
||||
DRM_DRV=""
|
||||
for _c in /sys/class/drm/card[0-9]*; do
|
||||
[ -r "$_c/device/uevent" ] || continue
|
||||
DRM_DRV=$(sed -n 's/^DRIVER=//p' "$_c/device/uevent" 2>/dev/null)
|
||||
[ -n "$DRM_DRV" ] && break
|
||||
done
|
||||
DRI=/usr/lib/dri
|
||||
case "$DRM_DRV" in
|
||||
i915) MESA_HW=iris ;;
|
||||
amdgpu) MESA_HW=radeonsi ;;
|
||||
radeon) MESA_HW=r600 ;;
|
||||
nouveau) MESA_HW=nouveau ;;
|
||||
*) MESA_HW="" ;;
|
||||
esac
|
||||
if [ -n "$MESA_HW" ] && [ -r "$DRI/${MESA_HW}_dri.so" ]; then
|
||||
say "GL por HARDWARE — kernel drm=$DRM_DRV + ${MESA_HW}_dri.so (sin overrides de software)"
|
||||
elif [ -r "$DRI/kms_swrast_dri.so" ] || [ -r "$DRI/swrast_dri.so" ]; then
|
||||
say "GL por SOFTWARE — drm=${DRM_DRV:-ninguno}, no hay userland HW ⇒ kms_swrast"
|
||||
# `GBM_ALWAYS_SOFTWARE` es lo que hace que mesa acepte crear un device GBM sobre virtio-gpu sin
|
||||
# acelerador; `LP_NUM_THREADS=1` evita que llvmpipe se coma los vcpus y deje al compositor sin
|
||||
# planificar. Las dos vienen pagas de las campañas KDE y GNOME.
|
||||
export GBM_ALWAYS_SOFTWARE=1 LIBGL_ALWAYS_SOFTWARE=1 MESA_LOADER_DRIVER_OVERRIDE=kms_swrast LP_NUM_THREADS=1
|
||||
else
|
||||
say "!! NI hardware NI software: drm=${DRM_DRV:-ninguno}; en $DRI sólo hay: $(ls $DRI 2>/dev/null | tr '\n' ' ')"
|
||||
say " el compositor va a fallar al inicializar EGL — ESTO es la causa, no un síntoma posterior"
|
||||
fi
|
||||
# COSMIC pinta con tiny-skia (libcosmic entra sin `wgpu`), pero el compositor SÍ usa EGL/GL por
|
||||
# smithay para componer, así que las de arriba siguen haciendo falta.
|
||||
# smithay para componer, así que el modo de GL lo decide todo.
|
||||
export RUST_BACKTRACE=1
|
||||
export RUST_LOG="${RUST_LOG:-info}"
|
||||
|
||||
@@ -70,13 +118,13 @@ export RUST_LOG="${RUST_LOG:-info}"
|
||||
# porque deja un log donde mirar. Si no sube, se cae al builtin: mejor un escritorio con andamio que
|
||||
# ninguno, pero se dice en el serial para que no pase por bueno.
|
||||
if [ -x /usr/bin/seatd ]; then
|
||||
/usr/bin/seatd -g video >/tmp/seatd.log 2>&1 &
|
||||
/usr/bin/seatd -g video >$LOG/seatd.log 2>&1 &
|
||||
SEATD_PID=$!
|
||||
sleep 1
|
||||
if alive $SEATD_PID && [ -S /run/seatd.sock ]; then
|
||||
say "seatd OK (pid $SEATD_PID, /run/seatd.sock)"
|
||||
else
|
||||
say "!! seatd no levantó — caigo a LIBSEAT_BACKEND=builtin (somos root)"; dump /tmp/seatd.log
|
||||
say "!! seatd no levantó — caigo a LIBSEAT_BACKEND=builtin (somos root)"; dump $LOG/seatd.log
|
||||
export LIBSEAT_BACKEND=builtin
|
||||
fi
|
||||
else
|
||||
@@ -96,9 +144,9 @@ if [ -x /usr/bin/dbus-daemon ]; then
|
||||
mkdir -p /run/dbus /var/lib/dbus
|
||||
[ -f /var/lib/dbus/machine-id ] || dbus-uuidgen > /var/lib/dbus/machine-id 2>/dev/null
|
||||
[ -f /etc/machine-id ] || cp /var/lib/dbus/machine-id /etc/machine-id 2>/dev/null
|
||||
dbus-daemon --system --fork >/tmp/dbus-system.log 2>&1
|
||||
dbus-daemon --system --fork >$LOG/dbus-system.log 2>&1
|
||||
sleep 1
|
||||
[ -S /run/dbus/system_bus_socket ] && say "bus de sistema OK" || { say "!! sin bus de sistema:"; dump /tmp/dbus-system.log; }
|
||||
[ -S /run/dbus/system_bus_socket ] && say "bus de sistema OK" || { say "!! sin bus de sistema:"; dump $LOG/dbus-system.log; }
|
||||
else
|
||||
say "!! sin dbus-daemon"
|
||||
fi
|
||||
@@ -108,7 +156,7 @@ fi
|
||||
# `org.freedesktop.login1` acá es arje-logind-compat. Se ESPERA el nombre, no el pid: un pid no es un
|
||||
# servicio, y esperar sólo al final deja pasar la carrera (lección de gnome-start, 2026-08-03).
|
||||
if [ -x /usr/bin/arje-logind-compat ]; then
|
||||
ARJE_LOGIN_STATE=1 /usr/bin/arje-logind-compat >/tmp/logind-compat.log 2>&1 &
|
||||
ARJE_LOGIN_STATE=1 /usr/bin/arje-logind-compat >$LOG/logind-compat.log 2>&1 &
|
||||
say "arje-logind-compat lanzado (pid $!)"
|
||||
i=0
|
||||
while [ $i -lt 20 ]; do
|
||||
@@ -118,7 +166,7 @@ if [ -x /usr/bin/arje-logind-compat ]; then
|
||||
fi
|
||||
i=$((i+1)); sleep 0.5
|
||||
done
|
||||
[ $i -lt 20 ] || { say "!! login1 NO apareció en 10s:"; dump /tmp/logind-compat.log; }
|
||||
[ $i -lt 20 ] || { say "!! login1 NO apareció en 10s:"; dump $LOG/logind-compat.log; }
|
||||
else
|
||||
say "!! sin arje-logind-compat — cosmic-comp puede no hallar sesión"
|
||||
fi
|
||||
@@ -154,7 +202,7 @@ mkdir -p "$XDG_RUNTIME_DIR" && chmod 700 "$XDG_RUNTIME_DIR"
|
||||
# el backend del portal crea su propio nodo de video— y para AUDIO no. Son dos preguntas distintas y
|
||||
# esta pieza sólo contesta la primera.
|
||||
if [ -x /usr/bin/pipewire ]; then
|
||||
/usr/bin/pipewire >/tmp/pipewire.log 2>&1 &
|
||||
/usr/bin/pipewire >$LOG/pipewire.log 2>&1 &
|
||||
PW_PID=$!
|
||||
# El socket, no el pid: misma disciplina que el compositor. `pipewire-0` es el nombre por defecto
|
||||
# (`core.name` en pipewire.conf); si el demonio muere al instante, el pid sigue "existiendo" un
|
||||
@@ -162,10 +210,10 @@ if [ -x /usr/bin/pipewire ]; then
|
||||
i=0
|
||||
while [ $i -lt 20 ]; do
|
||||
[ -S "$XDG_RUNTIME_DIR/pipewire-0" ] && { say "pipewire OK (socket pipewire-0, pid $PW_PID)"; break; }
|
||||
alive $PW_PID || { say "!! pipewire MURIÓ antes de abrir el socket:"; dump /tmp/pipewire.log; break; }
|
||||
alive $PW_PID || { say "!! pipewire MURIÓ antes de abrir el socket:"; dump $LOG/pipewire.log; break; }
|
||||
i=$((i+1)); sleep 0.5
|
||||
done
|
||||
[ -S "$XDG_RUNTIME_DIR/pipewire-0" ] || { say "!! pipewire sin socket en $((i/2))s:"; dump /tmp/pipewire.log; }
|
||||
[ -S "$XDG_RUNTIME_DIR/pipewire-0" ] || { say "!! pipewire sin socket en $((i/2))s:"; dump $LOG/pipewire.log; }
|
||||
else
|
||||
say "!! sin /usr/bin/pipewire — ScreenCast no va a poder crear stream"
|
||||
fi
|
||||
@@ -184,13 +232,13 @@ fi
|
||||
# espera un instante y se reporta si el proceso sigue vivo — con el log a mano, que es lo que sirve
|
||||
# cuando falla.
|
||||
if [ -x /usr/bin/wireplumber ] && [ -S "$XDG_RUNTIME_DIR/pipewire-0" ]; then
|
||||
/usr/bin/wireplumber >/tmp/wireplumber.log 2>&1 &
|
||||
/usr/bin/wireplumber >$LOG/wireplumber.log 2>&1 &
|
||||
WP_PID=$!
|
||||
sleep 2
|
||||
if alive $WP_PID; then
|
||||
say "wireplumber OK (pid $WP_PID) — hay gestor de sesión"
|
||||
else
|
||||
say "!! wireplumber MURIÓ:"; dump /tmp/wireplumber.log
|
||||
say "!! wireplumber MURIÓ:"; dump $LOG/wireplumber.log
|
||||
fi
|
||||
elif [ -x /usr/bin/wireplumber ]; then
|
||||
say "!! wireplumber presente pero sin socket de pipewire — no se lanza"
|
||||
@@ -256,15 +304,15 @@ fi
|
||||
# COSMIC_MODE=session para probar el camino de producción cuando el daemon exista.
|
||||
if [ "${COSMIC_MODE:-bare}" = session ]; then
|
||||
say "lanzando cosmic-session vía start-cosmic (modo session)"
|
||||
/bin/bash /usr/bin/start-cosmic --in-login-shell >/tmp/cosmic-session.log 2>&1 &
|
||||
/bin/bash /usr/bin/start-cosmic --in-login-shell >$LOG/cosmic-session.log 2>&1 &
|
||||
CS_PID=$!
|
||||
else
|
||||
say "lanzando cosmic-comp SOLO (modo bare; cosmic-session necesita settings-daemon)"
|
||||
dbus-daemon --session --fork --print-address=1 >/tmp/dbus-session-addr 2>/tmp/dbus-session.log
|
||||
DBUS_SESSION_BUS_ADDRESS=$(cat /tmp/dbus-session-addr 2>/dev/null)
|
||||
dbus-daemon --session --fork --print-address=1 >$LOG/dbus-session-addr 2>$LOG/dbus-session.log
|
||||
DBUS_SESSION_BUS_ADDRESS=$(cat $LOG/dbus-session-addr 2>/dev/null)
|
||||
[ -n "$DBUS_SESSION_BUS_ADDRESS" ] && export DBUS_SESSION_BUS_ADDRESS && say "bus de sesión OK" \
|
||||
|| { say "!! sin bus de sesión:"; dump /tmp/dbus-session.log; }
|
||||
/usr/bin/cosmic-comp >/tmp/cosmic-session.log 2>&1 &
|
||||
|| { say "!! sin bus de sesión:"; dump $LOG/dbus-session.log; }
|
||||
/usr/bin/cosmic-comp >$LOG/cosmic-session.log 2>&1 &
|
||||
CS_PID=$!
|
||||
fi
|
||||
|
||||
@@ -278,11 +326,11 @@ while [ $i -lt 60 ]; do
|
||||
say "compositor OK ($WD) — cosmic-comp ES el display server"
|
||||
break
|
||||
fi
|
||||
alive $CS_PID || { say "!! la sesión MURIÓ antes de crear el socket:"; dump /tmp/cosmic-session.log; break; }
|
||||
alive $CS_PID || { say "!! la sesión MURIÓ antes de crear el socket:"; dump $LOG/cosmic-session.log; break; }
|
||||
i=$((i+1)); sleep 1
|
||||
done
|
||||
[ -n "$WD" ] || say "!! sin socket wayland en ${i}s:"
|
||||
[ -n "$WD" ] || dump /tmp/cosmic-session.log
|
||||
[ -n "$WD" ] || dump $LOG/cosmic-session.log
|
||||
|
||||
# ── Los clientes, contra el socket que el compositor haya elegido ───────────────────────────────
|
||||
# En modo bare no hay quien los levante: `cosmic-session` es el que normalmente hace esto, y por eso
|
||||
@@ -293,7 +341,7 @@ if [ -n "$WD" ] && [ "${COSMIC_MODE:-bare}" != session ]; then
|
||||
|
||||
for c in cosmic-bg cosmic-panel cosmic-osd cosmic-notifications cosmic-app-library cosmic-launcher cosmic-idle; do
|
||||
if [ -x "/usr/bin/$c" ]; then
|
||||
"/usr/bin/$c" >"/tmp/$c.log" 2>&1 &
|
||||
"/usr/bin/$c" >"$LOG/$c.log" 2>&1 &
|
||||
say "cliente $c lanzado (pid $!)"
|
||||
sleep 1
|
||||
fi
|
||||
@@ -308,9 +356,9 @@ while [ $n -lt 12 ]; do
|
||||
# `pgrep -c` de busybox devolvía 0 con el compositor VIVO y su socket abierto — o sea que el
|
||||
# contador mentía. Se cuentan los /proc/*/comm, que es el dato del kernel y no una utilería.
|
||||
say "STATUS +$((n*15))s: $(for c in /proc/[0-9]*/comm; do cat "$c" 2>/dev/null; done | grep -c '^cosmic') procesos cosmic · $(for c in /proc/[0-9]*/comm; do cat "$c" 2>/dev/null; done | grep '^cosmic' | sort -u | tr '\n' ' ')"
|
||||
[ $n = 2 ] && { say "log del compositor (primer tramo)"; dump /tmp/cosmic-session.log; for l in /tmp/cosmic-bg.log /tmp/cosmic-panel.log; do [ -s "$l" ] && dump "$l"; done; }
|
||||
[ $n = 2 ] && { say "log del compositor (primer tramo)"; dump $LOG/cosmic-session.log; for l in $LOG/cosmic-bg.log $LOG/cosmic-panel.log; do [ -s "$l" ] && dump "$l"; done; }
|
||||
done
|
||||
|
||||
say "fin de la ventana de observación"
|
||||
dump /tmp/cosmic-session.log
|
||||
dump $LOG/cosmic-session.log
|
||||
exec /bin/sh
|
||||
|
||||
@@ -70,14 +70,98 @@ mkdir -p "$MERGED/usr/lib/musl/lib" "$MERGED/lib"
|
||||
cp -a "$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"
|
||||
|
||||
# ── LA PREPARACIÓN DE SESIÓN QUE ESTA IMAGEN NO TENÍA ───────────────────────────────────────────
|
||||
# Todo lo que sigue existía sólo en qemu-desktop-image.sh. La imagen de metal se armó copiando el
|
||||
# arranque pero NO esta parte, y el resultado es que el escritorio no podía funcionar en metal por
|
||||
# razones que nada tienen que ver con la GPU: sin `video` no arranca seatd, sin `messagebus` no
|
||||
# arranca el bus de SISTEMA — y sin bus de sistema NO HAY PORTAL, o sea que `portal-probe screencast`
|
||||
# no habría tenido con quién hablar aunque el GL fuese perfecto. Se descubrió corriendo `cosmic-start`
|
||||
# sobre la imagen de metal por primera vez (antes sólo se validaba que llegara al prompt).
|
||||
echo "==> inyectando arje-logind-compat (el login1 del fractal) + su política"
|
||||
if ALC="${ALC:-store/$(./target/release/hammer --store store hash recipes/arje-logind-compat.toml 2>/dev/null | tail -1 | sed 's/^b3://')-arje-logind-compat}"; [ -d "$ALC" ]; then
|
||||
install -Dm755 "$ALC"/usr/bin/arje-logind-compat "$MERGED"/usr/bin/arje-logind-compat
|
||||
install -d "$MERGED/usr/share/dbus-1/system.d"
|
||||
cp -a --remove-destination "$ALC"/usr/share/dbus-1/system.d/. "$MERGED"/usr/share/dbus-1/system.d/ 2>/dev/null || true
|
||||
echo " ✓ $(basename "$ALC" | cut -c1-12)"
|
||||
else
|
||||
echo " ✗ falta arje-logind-compat en store/ — cosmic-comp no hallará sesión" >&2; exit 1
|
||||
fi
|
||||
[ -f "$MERGED/usr/share/dbus-1/system.d/org.freedesktop.login1.conf" ] \
|
||||
|| { echo " ✗ falta org.freedesktop.login1.conf — re-sellá arje-logind-compat" >&2; exit 1; }
|
||||
|
||||
echo "==> grupo video (seatd lo exige por NOMBRE) y usuario messagebus (el bus de sistema hace setuid)"
|
||||
if ! grep -q '^video:' "$MERGED/etc/group" 2>/dev/null; then
|
||||
{ cat "$MERGED/etc/group" 2>/dev/null; echo "video:x:44:"; } > "$MERGED/etc/group.new"
|
||||
chmod 644 "$MERGED/etc/group.new"; mv "$MERGED/etc/group.new" "$MERGED/etc/group"
|
||||
fi
|
||||
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
|
||||
{ cat "$MERGED/etc/$file" 2>/dev/null; echo "$line"; } > "$MERGED/etc/$file.new"
|
||||
chmod 644 "$MERGED/etc/$file.new"; mv "$MERGED/etc/$file.new" "$MERGED/etc/$file"
|
||||
fi
|
||||
done
|
||||
for h in usr/libexec/dbus-daemon-launch-helper usr/lib/dbus-1.0/dbus-daemon-launch-helper; do
|
||||
[ -f "$MERGED/$h" ] && { chown 0:81 "$MERGED/$h" 2>/dev/null || true; chmod 4750 "$MERGED/$h"; }
|
||||
done
|
||||
grep -q '^video:' "$MERGED/etc/group" && grep -q '^messagebus:' "$MERGED/etc/passwd" \
|
||||
|| { echo " ✗ video/messagebus no quedaron en /etc — seatd y dbus fallarían" >&2; exit 1; }
|
||||
echo " ✓ video + messagebus"
|
||||
|
||||
# arje-logind-compat busca el bus de sistema en la ruta LEGACY /var/run y dbus-daemon escucha en /run.
|
||||
# Esta base metal es tan mínima que ni siquiera tiene /var/run: sin el enlace, el daemon se va a idle
|
||||
# y nunca registra login1.
|
||||
ln -sfn /run "$MERGED/var/run"
|
||||
printf 'COSMIC_MODE=%s\n' "${COSMIC_MODE:-bare}" > "$MERGED/etc/cosmic-mode"
|
||||
|
||||
# ── el arranque del escritorio ──────────────────────────────────────────────────────────────────
|
||||
# Se instala EL MISMO script que se validó en QEMU, y no una variante: nada de lo que hace es
|
||||
# específico del emulador. Levanta XDG_RUNTIME_DIR, seatd, el bus de sistema, el shim de logind,
|
||||
# pipewire, wireplumber, el compositor y los clientes — todos pasos de escritorio, no de QEMU. Que
|
||||
# corra el mismo fichero es lo que hace que «validado en QEMU» signifique algo para el metal.
|
||||
echo "==> instalando /usr/bin/cosmic-start (el MISMO script validado en QEMU)"
|
||||
install -Dm755 scripts/cosmic/cosmic-start-qemu.sh "$MERGED/usr/bin/cosmic-start"
|
||||
# Se instala EL MISMO script que corre en QEMU. Eso es deseable —hace que «validado en QEMU»
|
||||
# signifique algo para el metal— pero SÓLO si el script no tiene supuestos del emulador escondidos.
|
||||
#
|
||||
# ⚠ ACÁ HABÍA UN COMENTARIO QUE AFIRMABA QUE NO LOS TENÍA, Y ERA FALSO. El script exportaba
|
||||
# `LIBGL_ALWAYS_SOFTWARE=1` y `MESA_LOADER_DRIVER_OVERRIDE=kms_swrast` incondicionalmente, que es
|
||||
# correcto sobre virtio-gpu y letal sobre un Intel real: la mesa del corpus es `iris` y no existe
|
||||
# ningún `kms_swrast_dri.so` que cargar ⇒ EGL falla y el compositor no arranca. Costó un viaje
|
||||
# físico el 2026-08-05. Ahora el script DETECTA el modo de GL, y esta imagen lo verifica abajo.
|
||||
echo "==> instalando /usr/bin/cosmic-start (detecta HW/software; ya no fuerza kms_swrast)"
|
||||
install -Dm755 scripts/cosmic/cosmic-start.sh "$MERGED/usr/bin/cosmic-start"
|
||||
grep -q "MESA_LOADER_DRIVER_OVERRIDE=kms_swrast$" "$MERGED/usr/bin/cosmic-start" && {
|
||||
echo " ✗ cosmic-start todavía fuerza kms_swrast incondicionalmente — esta imagen sería inútil" >&2; exit 1; }
|
||||
|
||||
# ── firmware i915 para OTRAS generaciones Intel, no sólo la del laptop ───────────────────────────
|
||||
# La base metal trae sólo `tgl_*` (TigerLake). En el Coffee Lake del OptiPlex 3060 el kernel pidió
|
||||
# `i915/kbl_dmc_ver1_04.bin` y no estaba. NO es fatal —el propio kernel dice «Disabling runtime power
|
||||
# management» y sigue haciendo modeset— pero es gratis arreglarlo y evita perseguir un fantasma.
|
||||
FWSRC="${FWSRC:-/usr/lib/firmware/i915}"
|
||||
if [ -d "$FWSRC" ]; then
|
||||
n=0
|
||||
for f in "$FWSRC"/{kbl,skl,cfl,icl,ehl,jsl,adl,dg}*.bin*; do
|
||||
[ -r "$f" ] || continue
|
||||
b="$(basename "$f")"
|
||||
case "$b" in
|
||||
*.zst) zstd -qdf "$f" -o "$MERGED/lib/firmware/i915/${b%.zst}" 2>/dev/null && n=$((n+1)) ;;
|
||||
*) cp -a "$f" "$MERGED/lib/firmware/i915/" && n=$((n+1)) ;;
|
||||
esac
|
||||
done
|
||||
echo "==> firmware i915: +$n blobs de otras generaciones (la base sólo traía tgl_*)"
|
||||
else
|
||||
echo " ⚠ sin $FWSRC — la imagen sólo servirá plenamente en TigerLake"
|
||||
fi
|
||||
|
||||
# ── SSH: la puerta trasera que convierte un viaje físico en una sesión remota ────────────────────
|
||||
# sshd ya arrancaba y escuchaba en :22, pero era INALCANZABLE: `PasswordAuthentication no` y sin
|
||||
# /root/.ssh/authorized_keys. O sea que en metal sólo quedaba depurar a ciegas sobre la pantalla.
|
||||
# Con una clave horneada, el próximo fallo se mira por red y se itera sin quemar el USB de nuevo.
|
||||
AKEY="${AKEY:-work/metal-authorized_keys}"
|
||||
if [ -r "$AKEY" ]; then
|
||||
install -Dm600 "$AKEY" "$MERGED/root/.ssh/authorized_keys"
|
||||
chmod 700 "$MERGED/root/.ssh"
|
||||
echo "==> SSH: authorized_keys horneada ($(wc -l < "$AKEY") clave/s) — root por clave, sin password"
|
||||
else
|
||||
echo " ⚠ sin $AKEY ⇒ sshd escuchará pero será inalcanzable (depuración sólo a ciegas)"
|
||||
fi
|
||||
|
||||
# ── getty en tty1: el prompt TIENE que salir por la PANTALLA ────────────────────────────────────
|
||||
# Lección del 1er viaje físico de KDE, que costó un USB quemado: el CMDLINE horneado es
|
||||
@@ -99,6 +183,29 @@ export HOME=/root TERM=linux
|
||||
exec /bin/sh
|
||||
LOGIN
|
||||
chmod 0755 "$MERGED/usr/bin/console-login"
|
||||
|
||||
# ── netup pedía DHCP antes de que existiera el enlace ────────────────────────────────────────────
|
||||
# Medido en el dmesg del OptiPlex 3060: el r8169 levanta el enlace a los 20,2 s (con downshift a
|
||||
# 100 Mbps), y el ente sshd corría `netup` a los 13,7 s ⇒ DHCP sobre un cable que aún no estaba y
|
||||
# «sin respuesta DHCP» para siempre, sin reintento. En QEMU no se ve: el virtio-net tiene carrier
|
||||
# desde el instante cero. Sin red no hay SSH, y sin SSH el metal se depura a ciegas.
|
||||
cat > "$MERGED/usr/bin/netup-wait" <<'NETUP'
|
||||
#!/bin/sh
|
||||
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
||||
i=0
|
||||
while [ $i -lt 40 ]; do
|
||||
for c in /sys/class/net/*/carrier; do
|
||||
case "$c" in */lo/*) continue ;; esac # lo siempre dice 1: no es señal de nada
|
||||
[ -r "$c" ] || continue
|
||||
[ "$(cat "$c" 2>/dev/null)" = 1 ] && exec /usr/bin/netup
|
||||
done
|
||||
i=$((i + 1)); sleep 1
|
||||
done
|
||||
echo "netup-wait: ninguna interfaz con carrier tras ${i}s — pido DHCP igual"
|
||||
exec /usr/bin/netup
|
||||
NETUP
|
||||
chmod 0755 "$MERGED/usr/bin/netup-wait"
|
||||
|
||||
python3 - "$MERGED/ente/seed.card.json" <<'PY'
|
||||
# OJO: $MERGED es un árbol de HARDLINKS a la base metal ⇒ escribir in-place mutaría también
|
||||
# work/metal-rootfs (y toda otra imagen que comparta el inodo). Se escribe a un temporal y se
|
||||
@@ -114,6 +221,11 @@ tty1["id"] = "01HQAR53D4M2NBV8KZTYXFQA04"
|
||||
tty1["label"] = "tty1-getty"
|
||||
tty1["payload"]["Native"]["argv"] = ["getty", "-n", "-l", "/usr/bin/console-login", "38400", "tty1"]
|
||||
gen.append(tty1)
|
||||
# el ente sshd espera al carrier antes de pedir DHCP (ver netup-wait); idempotente
|
||||
for g in gen:
|
||||
if g["label"] == "sshd":
|
||||
a = g["payload"]["Native"]["argv"]
|
||||
a[:] = [x.replace("/usr/bin/netup;", "/usr/bin/netup-wait;") for x in a]
|
||||
json.dump(card, open(p + ".new", "w"), indent=2, sort_keys=True)
|
||||
os.replace(p + ".new", p)
|
||||
print(" ✓ genesis: " + ", ".join(g["label"] for g in gen))
|
||||
@@ -130,10 +242,13 @@ cat > "$MERGED/etc/motd" <<'MOTD'
|
||||
dmabuf.
|
||||
|
||||
Arrancar el escritorio: cosmic-start
|
||||
⇒ tiene que decir "GL por HARDWARE". Si dice SOFTWARE, el viaje no
|
||||
sirve: ScreenCast fallaría igual que en QEMU.
|
||||
Probar el portal: portal-probe version
|
||||
portal-probe screencast <-- LO QUE VINIMOS A VER
|
||||
GPU detectada: ls /dev/dri ; dmesg | grep -iE 'i915|drm'
|
||||
Logs si algo falla: /tmp/cosmic-session.log /tmp/pipewire.log
|
||||
Logs (en DISCO, sobreviven al apagón): /var/log/cosmic/
|
||||
Red y SSH: netup-wait ; ip addr (root por clave, sin password)
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
MOTD
|
||||
|
||||
@@ -123,7 +123,7 @@ printf 'COSMIC_MODE=%s\n' "${COSMIC_MODE:-bare}" > "$MERGED/etc/cosmic-mode"
|
||||
echo "==> modo de arranque: ${COSMIC_MODE:-bare}"
|
||||
|
||||
echo "==> instalando /usr/bin/cosmic-start"
|
||||
install -Dm755 scripts/cosmic/cosmic-start-qemu.sh "$MERGED/usr/bin/cosmic-start"
|
||||
install -Dm755 scripts/cosmic/cosmic-start.sh "$MERGED/usr/bin/cosmic-start"
|
||||
|
||||
echo "==> auto-lanzar COSMIC: console-getty ejecuta cosmic-start (no /bin/sh)"
|
||||
python3 - "$MERGED/ente/seed.card.json" <<'PY'
|
||||
|
||||
Reference in New Issue
Block a user