Cierra un TODO que el propio script tenía escrito («esto pertenece al artefacto de arje-logind-compat, no a la imagen») y que estaba bien puesto: **un servicio que no puede adueñarse de su nombre no es un servicio**, así que su política D-Bus es parte de lo que el paquete promete, igual que el binario. Con la política en el script de UNA imagen, cualquier otra —metal, KDE, mirada— se llevaba el daemon y no podía usarlo. arje-logind-compat b3:b4795829 · arje-polkit-compat b3:6c82f44b. Radio de las dos: 0. **Y la comprobación que reemplaza a escribirlas atrapó un bug de verdad en el primer intento.** Mover algo a un artefacto sólo es una mejora si se NOTA cuando falta, así que el script pasó de ESCRIBIR los .conf a EXIGIRLOS. Falló al toque: `✗ falta org.freedesktop.login1.conf`. Causa — el script inyectaba **sólo el binario** del artefacto (`install -Dm755 .../usr/bin/...`), así que el .conf existía en el store y nunca llegaba a la imagen. Sin esa comprobación habría sido el fallo tardío de siempre: daemon que arranca, no adquiere el nombre, y 25s de espera por servicio sin decir por qué. Verificado de punta a punta con las políticas viniendo del artefacto: los cuatro nombres del bus arriba (login1, PolicyKit1, Accounts, UPower) y el audio intacto. El `zz-` de la de polkit no es decorativo y queda explicado en su receta: a diferencia de login1, ese nombre YA tiene política —la trae el artefacto de polkit— y permite `own` sólo al usuario polkitd; dbus lee system.d en orden alfabético y las reglas posteriores ganan, así que un fichero que ordene después AÑADE el permiso sin descartar el resto de upstream. ColorManager sigue sin aparecer: es lo de colord, ya diagnosticado hasta dónde llega y fuera del alcance de este cambio. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
225 lines
13 KiB
Bash
Executable File
225 lines
13 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"/
|
|
|
|
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 `hammer 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/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
|
|
# …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/hammer --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
|
|
|
|
# /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
|