Los demonios de sistema pasan de lanzarse con `&` desde un script de 500 líneas a ser Cards del `genesis`. El bloqueo que este frente daba por corpus se levantó solo: otro agente selló evolution-data-server y gnome-shell mientras esto se escribía, y escritorio-gnome quedó 309/309. SE CORRIÓ UN CONTROL PRIMERO, y es lo único que hace interpretable el resultado: colord control: `MURIÓ al arrancar` cards: `ya vive (pid 175)` ColorManager control: `NO apareció en 40s` cards: `OK` login1/Accounts/UPower OK en los dos compositor wayland-0 y shell vivo en los dos El fallo del control DESAPARECIÓ, y no lo buscaba: colord moría arrancado por el script y vive arrancado por arje, con el bus ya listo porque la espera está dentro de su argv. Sin el control, «ColorManager OK» sería un dato suelto en vez de una diferencia. Los PIDs lo confirman: polkit=119, colord=175, upowerd=178, accounts=180 — de antes de que el lanzador de sesión existiera. QUÉ NO SE COMPROBÓ: no hay screendump; QEMU salió por timeout y el control tampoco lo tuvo. La comparación es serial contra serial y lo que se afirma es sobre los DEMONIOS, no sobre el pintado. Las piezas donde corresponde: `takana service-cards` (UNA sola implementación de receta→Card; el formato es contrato con card_core::Card), `targets.py --service-paths` (une qué-es con si-arranca), y un inyector en FICHERO APARTE porque anidar dos heredocs de python falló en vivo — el terminador del interno cerró el externo y media cosa corrió como shell. La espera del bus va DENTRO del argv de las 5 recetas de sistema: sin ella un daemon arranca antes de que dbus escuche y queda en modo idle sin registrar su nombre — un fallo que no se ve, porque el proceso vive y el bus no lo tiene. Los 5 hashes intactos. Y el guardia de gnome-start es por «¿está corriendo?», no por una perilla: así es correcto venga de donde venga el proceso y la misma copia sirve donde no se inyectaron cards.
244 lines
14 KiB
Bash
Executable File
244 lines
14 KiB
Bash
Executable File
#!/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"/
|
|
|
|
. "$ROOT/scripts/lib/pid1-desde-store.sh"
|
|
. "$ROOT/scripts/lib/libs-compartidas-desde-store.sh"
|
|
inyectar_pid1 "$MERGED" "$BASE"
|
|
inyectar_libs_compartidas "$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.
|
|
# El artefacto se elige por `takana hash` sobre la receta —el que corresponde a la receta de HOY—, no
|
|
# por el más reciente del store: con dos artefactos del mismo paquete conviviendo, la fecha no dice
|
|
# cuál es el vigente. ALC_BIN permite inyectar un binario RECIÉN COMPILADO en vez del artefacto sellado. Es para probar
|
|
# un cambio en arje-compat antes de commitear/pushear tawasuyu y re-sellar la receta (la receta pina
|
|
# un commit, así que sin push no hay artefacto nuevo). Lo que se pruebe así NO es reproducible: es
|
|
# andamio de iteración, y el camino de producción sigue siendo el artefacto del store.
|
|
if [ -n "${ALC_BIN:-}" ] && [ -x "$ALC_BIN" ]; then
|
|
install -Dm755 "$ALC_BIN" "$MERGED"/usr/bin/arje-logind-compat
|
|
echo " ⚠ binario LOCAL (no sellado): $ALC_BIN"
|
|
elif ALC="${ALC:-store/$(./target/release/takana --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
|
|
# …y su POLÍTICA D-Bus, que desde 2026-07-30 viaja en el artefacto. Se copia, no se escribe: el
|
|
# dueño del fichero es el paquete. Ojo — inyectar «sólo el binario» era el bug que destapó la
|
|
# comprobación de abajo: el artefacto traía el .conf y la imagen lo dejaba afuera.
|
|
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/ — mutter no hallará sesión" >&2; exit 1
|
|
fi
|
|
|
|
# La POLÍTICA D-Bus de login1 YA NO SE ESCRIBE ACÁ (2026-07-30): la trae el artefacto de
|
|
# `arje-logind-compat`, que es su dueño. Este bloque tenía un TODO diciendo exactamente eso, y estaba
|
|
# bien puesto: un servicio que no puede adueñarse de su nombre no es un servicio, así que su política
|
|
# es parte de lo que el paquete promete. Teniéndola acá, cualquier OTRA imagen —metal, KDE, mirada—
|
|
# se llevaba el daemon y no podía usarlo. Se comprueba abajo, junto con la de polkit.
|
|
install -d "$MERGED/usr/share/dbus-1/system.d"
|
|
|
|
echo "==> inyectando arje-polkit-compat (el PolicyKit1 del fractal) + su política"
|
|
# EL TERCER ESLABÓN DEL MISMO TIPO, y lo destapó la lista de nombres del bus: accounts-daemon y
|
|
# upowerd quedaban CONECTADOS (`:1.0`, `:1.1`) sin adquirir su nombre, porque los dos se bloquean en
|
|
# `polkit_authority_get_sync()` esperando a `org.freedesktop.PolicyKit1`. Nuestra receta `polkit` es
|
|
# libs-only a propósito (el demonio lo pone arje), así que nadie servía ese nombre y gnome-shell
|
|
# esperaba 25 s por cada uno.
|
|
# ⚠ El shim autoriza TODO (postura de sistema confiado ya tomada por arje) — ver su receta.
|
|
if APC="${APC:-store/$(./target/release/takana --store store hash recipes/arje-polkit-compat.toml 2>/dev/null | tail -1 | sed 's/^b3://')-arje-polkit-compat}"; [ -d "$APC" ]; then
|
|
install -Dm755 "$APC"/usr/bin/arje-polkit-compat "$MERGED"/usr/bin/arje-polkit-compat
|
|
install -d "$MERGED/usr/share/dbus-1/system.d"
|
|
cp -a --remove-destination "$APC"/usr/share/dbus-1/system.d/. "$MERGED"/usr/share/dbus-1/system.d/ 2>/dev/null || true
|
|
echo " ✓ $(basename "$APC" | cut -c1-12)"
|
|
else
|
|
echo " ✗ falta arje-polkit-compat en store/ — accounts-daemon y upowerd no adquirirán su nombre" >&2
|
|
exit 1
|
|
fi
|
|
# Su política tampoco se escribe acá: viaja en el artefacto (ver recipes/arje-polkit-compat.toml, que
|
|
# explica por qué el fichero se llama `zz-` y no reemplaza al de polkit).
|
|
#
|
|
# ── COMPROBACIÓN, que es lo que reemplaza a escribirlas ────────────────────────────────────────
|
|
# Mover algo a un artefacto sólo es una mejora si se NOTA cuando falta. Sin esto, un artefacto viejo
|
|
# sin su .conf daría el mismo fallo tardío y confuso de antes: el daemon arranca, no adquiere el
|
|
# nombre, y el escritorio espera 25 s por servicio sin decir por qué.
|
|
for pol in org.freedesktop.login1.conf zz-arje-polkit-compat.conf; do
|
|
if [ -f "$MERGED/usr/share/dbus-1/system.d/$pol" ]; then
|
|
echo " ✓ $pol (del artefacto)"
|
|
else
|
|
echo " ✗ falta $pol — el artefacto que lo trae es viejo; re-sellá su receta" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
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
|
|
|
|
# El launch-helper del bus de SISTEMA tiene que ser setuid root. Es el que dbus-daemon ejecuta para
|
|
# activar un servicio bajo demanda, y comprueba SUS PROPIOS permisos antes de hacer nada: si no es
|
|
# setuid root, se niega, y el cliente recibe
|
|
# Error calling StartServiceByName for <servicio>: The permission of the setuid helper is not correct
|
|
# que es exactamente lo que reportaba colord al arrancar la sesión. Sin esto **ninguna** activación
|
|
# por bus de sistema funciona; con esto, cualquier servicio con su `.service` instalado arranca solo y
|
|
# no hace falta lanzarlo a mano desde `gnome-start`.
|
|
for h in usr/libexec/dbus-daemon-launch-helper usr/lib/dbus-1.0/dbus-daemon-launch-helper; do
|
|
if [ -f "$MERGED/$h" ]; then
|
|
chown 0:81 "$MERGED/$h" 2>/dev/null || true # root:messagebus
|
|
chmod 4750 "$MERGED/$h"
|
|
echo " ✓ setuid en /$h"
|
|
fi
|
|
done
|
|
|
|
# GNOME_MODE se hornea como fichero: el getty ejecuta gnome-start sin ambiente heredable.
|
|
printf 'GNOME_MODE=%s\n' "${GNOME_MODE:-drm}" > "$MERGED/etc/gnome-mode"
|
|
echo "==> modo de arranque: ${GNOME_MODE:-drm}"
|
|
# KMS_MODE viaja por el mismo camino, y sólo si se pide: `gnome-start` lo traduce a
|
|
# MUTTER_DEBUG_FORCE_KMS_MODE. Vacío = atomic (el default de mutter). `simple` = el modeset legacy
|
|
# que se heredó de KDE y dejaba la pantalla en negro.
|
|
if [ -n "${KMS_MODE:-}" ]; then
|
|
printf 'KMS_MODE=%s\n' "$KMS_MODE" >> "$MERGED/etc/gnome-mode"
|
|
echo "==> KMS forzado a: $KMS_MODE"
|
|
fi
|
|
|
|
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
|
|
|
|
echo "==> inyectando las Cards de servicio en el genesis (SDD 30 4c)"
|
|
# LO QUE ESTO CAMBIA: hasta hoy los demonios de sistema los lanzaba `gnome-start` con `&` -- sin
|
|
# supervision, sin backoff y sin el `CRASHED` real, o sea sin nada de lo que arje es PID 1 para dar.
|
|
# Aca pasan a ser Cards del `genesis`, que es de donde arje los encarna y revive.
|
|
#
|
|
# Las dos mitades salen de donde viven: QUE es cada servicio, de `[[service]]` en su receta; CUALES
|
|
# arranca esta imagen, de `servicios = [...]` del perfil en targets.toml. La traduccion receta->Card
|
|
# la hace `takana service-cards` y no este script: el formato es contrato con `card_core::Card`, y
|
|
# dos generadores del mismo formato divergen en silencio.
|
|
CARDS_JSON="${CARDS_JSON:-$(mktemp)}"
|
|
./target/release/takana service-cards $(python3 scripts/targets.py --service-paths escritorio-gnome) > "$CARDS_JSON"
|
|
[ -s "$CARDS_JSON" ] || { echo " x no pude emitir las cards de servicio" >&2; exit 1; }
|
|
python3 scripts/gnome/inyectar-cards.py "$MERGED/ente/seed.card.json" "$CARDS_JSON"
|
|
|
|
# /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
|