From 08fc1d4b92d5f6391fcbcdf361f6fa068af6fdad Mon Sep 17 00:00:00 2001 From: sergio Date: Wed, 29 Jul 2026 11:54:52 -0400 Subject: [PATCH] =?UTF-8?q?gnome:=20accountsservice=20SELLA=20(b3:0328b0d0?= =?UTF-8?q?)=20=E2=80=94=20cae=20la=20frontera=20de=20la=20C-ABI=20de=20lo?= =?UTF-8?q?gind?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Los dos huecos que bloqueaban esta receta estaban medidos desde ayer símbolo por símbolo, y ninguno era de accountsservice. Los dos cerrados: 1. libelogind pasa de 14 a 22 símbolos (tawasuyu 8d892151b, re-pineado a b56aeff46, re-sellado b3:9650ee06). Los ocho nuevos son los que usa accountsservice, con `sd_login_monitor_*` implementado sobre inotify en /run/systemd/{sessions,seats,users}. 2. `fgetspent_r` no existe en musl ⇒ accountsservice-fgetspent_r-musl.patch. NO es sustituir por `fgetspent()` a secas: daemon.c guarda los buffers en un GHashTable y `fgetspent()` devuelve un struct estático que se reescribe en cada llamada — todas las entradas de la tabla apuntarían al último usuario leído. El parche copia el registro al buffer del llamador, y sigue el patrón que el propio accountsservice ya usa para /etc/passwd (`src/fgetpwent.c` bajo `#ifndef HAVE_FGETPWENT`). Produce `AccountsService-1.0.typelib`, que es exactamente lo que la capa JS pedía. De paso, dos cosas que este frente enseñó y quedan horneadas: - libelogind necesitaba `cargo_vendor_dir` (el `vendor/` de smithay entró a la rama selfhost con el merge de main; el mismo choque que ya tenía arje-logind-compat). - `hydrate-gnome.sh` ahora hidrata DOS raíces por defecto. accountsservice no es dep de build de nadie: es dep de RUNTIME, resuelta por gjs al arrancar. **El cierre de build no es el cierre de runtime**, y lo que el shell carga por `imports.gi.*` hay que nombrarlo a mano o no entra al rootfs. Co-Authored-By: Claude Opus 5 (1M context) --- .../accountsservice-fgetspent_r-musl.patch | 103 ++++++++++++++++++ recipes/incoming-gnome/accountsservice.toml | 33 +++--- recipes/incoming-gnome/libelogind.toml | 9 +- scripts/gnome/hydrate-gnome.sh | 12 +- 4 files changed, 140 insertions(+), 17 deletions(-) create mode 100644 recipes/incoming-gnome/accountsservice-fgetspent_r-musl.patch diff --git a/recipes/incoming-gnome/accountsservice-fgetspent_r-musl.patch b/recipes/incoming-gnome/accountsservice-fgetspent_r-musl.patch new file mode 100644 index 00000000..9ca27b18 --- /dev/null +++ b/recipes/incoming-gnome/accountsservice-fgetspent_r-musl.patch @@ -0,0 +1,103 @@ +accountsservice bajo musl: `fgetspent_r` no existe. + +QUÉ ES: la variante reentrante de la API de /etc/shadow. Es **extensión de glibc** — musl provee +`fgetspent()` (no reentrante) y `getspnam_r()`, pero no ésta. La usa `src/daemon.c` en el bucle que +lee /etc/shadow para decidir qué cuentas son locales. + +POR QUÉ NO ALCANZA CON USAR `fgetspent()` A SECAS: el llamador GUARDA los buffers. Cada iteración +reserva un `shadow_entry_buffers` y lo mete en un GHashTable indexado por nombre de usuario; después +de leer el fichero entero, consulta esa tabla. `fgetspent()` devuelve un puntero a un `struct spwd` +ESTÁTICO cuyas cadenas se reescriben en la llamada siguiente, así que sustituirla a secas dejaría +todas las entradas de la tabla apuntando al último usuario leído. Silencioso y falso — justo lo que +no se puede permitir un servicio que responde "¿esta cuenta es local?". + +LO QUE HACE EL PARCHE: implementa `fgetspent_r()` sobre `fgetspent()`, copiando las dos cadenas del +registro (`sp_namp`, `sp_pwdp`) al buffer que pasa el llamador y los campos numéricos por valor. Con +eso cada entrada de la tabla es dueña de su copia y la semántica vuelve a ser la de glibc. + +DÓNDE VIVE: en `src/fgetspent_r.c`, incluido desde `daemon.c` bajo `#ifndef HAVE_FGETSPENT_R`. **Es +el patrón que accountsservice YA usa**: trae un `src/fgetpwent.c` propio incluido bajo +`#ifndef HAVE_FGETPWENT` para el mismo problema con /etc/passwd (upstream lo escribió para plataformas +sin esa función). O sea que el parche no inventa una estructura: extiende la que el proyecto eligió. +El símbolo `HAVE_FGETSPENT_R` no lo define nunca su meson, así que la rama entra siempre; se deja el +`#ifndef` igual, para que el día que lo definan esto se apague solo. + +CONTRATO: 0 si leyó un registro; `ENOENT` en fin de fichero; `ERANGE` si el buffer del llamador se +queda corto — los mismos valores que devuelve glibc, que es lo que `daemon.c` espera (`ret == 0`, y +si no corta el bucle salvo EINTR). + +diff -Naur a/src/daemon.c b/src/daemon.c +--- a/src/daemon.c ++++ b/src/daemon.c +@@ -175,6 +175,10 @@ + #include "fgetpwent.c" + #endif + ++#ifndef HAVE_FGETSPENT_R ++#include "fgetspent_r.c" ++#endif ++ + #ifndef MAX_LOCAL_USERS + #define MAX_LOCAL_USERS 50 + #endif +diff -Naur a/src/fgetspent_r.c b/src/fgetspent_r.c +--- a/src/fgetspent_r.c 1969-12-31 20:00:00.000000000 -0400 ++++ b/src/fgetspent_r.c +@@ -0,0 +1,57 @@ ++/* fgetspent_r() para musl — ver recipes/incoming-gnome/accountsservice-fgetspent_r-musl.patch ++ * ++ * musl no trae esta extension de glibc. Se implementa sobre fgetspent(), que si tiene, copiando ++ * el registro al buffer del llamador: daemon.c GUARDA esos buffers en un GHashTable, asi que no ++ * puede quedarse con el struct estatico que fgetspent() reusa en cada llamada. ++ */ ++ ++static int ++fgetspent_r (FILE *fp, ++ struct spwd *result_buf, ++ char *buffer, ++ size_t buflen, ++ struct spwd **result) ++{ ++ struct spwd *ent; ++ size_t namp_len; ++ size_t pwdp_len; ++ size_t needed; ++ ++ if (result != NULL) { ++ *result = NULL; ++ } ++ if (fp == NULL || result_buf == NULL || buffer == NULL || result == NULL) { ++ return EINVAL; ++ } ++ ++ errno = 0; ++ ent = fgetspent (fp); ++ if (ent == NULL) { ++ /* Fin de fichero (o linea invalida): glibc devuelve ENOENT. */ ++ return errno != 0 ? errno : ENOENT; ++ } ++ ++ namp_len = ent->sp_namp != NULL ? strlen (ent->sp_namp) + 1 : 0; ++ pwdp_len = ent->sp_pwdp != NULL ? strlen (ent->sp_pwdp) + 1 : 0; ++ needed = namp_len + pwdp_len; ++ if (needed > buflen) { ++ return ERANGE; ++ } ++ ++ *result_buf = *ent; ++ result_buf->sp_namp = NULL; ++ result_buf->sp_pwdp = NULL; ++ ++ if (namp_len > 0) { ++ memcpy (buffer, ent->sp_namp, namp_len); ++ result_buf->sp_namp = buffer; ++ buffer += namp_len; ++ } ++ if (pwdp_len > 0) { ++ memcpy (buffer, ent->sp_pwdp, pwdp_len); ++ result_buf->sp_pwdp = buffer; ++ } ++ ++ *result = result_buf; ++ return 0; ++} diff --git a/recipes/incoming-gnome/accountsservice.toml b/recipes/incoming-gnome/accountsservice.toml index f77b794e..9371c7bf 100644 --- a/recipes/incoming-gnome/accountsservice.toml +++ b/recipes/incoming-gnome/accountsservice.toml @@ -50,27 +50,29 @@ # # ISLA DINÁMICA: -Dintrospection=true + shared, porque lo que falta es justamente el TYPELIB. # -# ══ ⛔ NO SELLA TODAVÍA — FRONTERA MEDIDA, NO ADIVINADA (2026-07-29) ═════════════════════════════ -# El configure pasa entero (los tres seds y las opciones de arriba están verificados contra el build -# real); lo que corta es el COMPILE, y por dos motivos independientes: +# ══ LOS DOS HUECOS QUE HUBO QUE TAPAR, Y NINGUNO ERA DE accountsservice ═════════════════════════ +# El configure pasaba entero desde el principio; lo que cortaba era el COMPILE, por dos motivos +# independientes, los dos de NUESTRA capa de compatibilidad. Se cuentan acá porque el trabajo real +# de esta receta está afuera de ella: # -# 1. LA C-ABI DE sd-login SE QUEDA CORTA. `libelogind` (arje-sdlogin-compat) exporta 14 símbolos — -# los que necesitaba mutter— y accountsservice usa OCHO que no están: +# 1. LA C-ABI DE sd-login SE QUEDABA CORTA. `libelogind` (arje-sdlogin-compat) exportaba 14 símbolos +# —los que necesitaba mutter— y accountsservice usa OCHO que no estaban: # sd_get_sessions · sd_seat_can_multi_session · sd_session_get_display # sd_login_monitor (el tipo) · sd_login_monitor_new · sd_login_monitor_unref # sd_login_monitor_get_fd · sd_login_monitor_flush -# Los cinco de `sd_login_monitor_*` son la parte con enjundia: no son getters sobre ficheros sino -# una API de NOTIFICACIÓN (un fd que se puede meter en un poll y despierta cuando cambia el -# estado de sesiones). Sobre el diseño actual —leer /run/systemd/{sessions,seats,users}— el -# camino natural es un inotify sobre esos directorios. +# Los cinco de `sd_login_monitor_*` eran la parte con enjundia: no son getters sobre ficheros sino +# una API de NOTIFICACIÓN (un fd que se mete en un poll y despierta cuando cambia el estado de +# sesiones). Implementados con inotify sobre /run/systemd/{sessions,seats,users}, que es donde +# vive ese estado. Ahora libelogind exporta 22. # -# 2. `fgetspent_r` NO EXISTE EN musl. Es la variante reentrante de la API de /etc/shadow, extensión -# de glibc. La usa src/daemon.c:265 para saber si una cuenta tiene contraseña. Necesita un shim -# o un parche que use la variante no-reentrante. +# 2. `fgetspent_r` NO EXISTE EN musl (extensión de glibc; la usa src/daemon.c:265). Se tapa con +# `accountsservice-fgetspent_r-musl.patch`, que la implementa sobre el `fgetspent()` que musl SÍ +# tiene, copiando el registro al buffer del llamador. La copia no es adorno: daemon.c GUARDA esos +# buffers en un GHashTable, y `fgetspent()` devuelve un struct estático que se reescribe en cada +# llamada. El parche sigue el patrón que el propio accountsservice ya usa para /etc/passwd +# (`src/fgetpwent.c` incluido bajo `#ifndef HAVE_FGETPWENT`). # -# NINGUNO DE LOS DOS es de accountsservice: son huecos de NUESTRA capa de compatibilidad. La receta -# queda como mapa honesto de la cadena —el mismo criterio que gnome-session y gnome-settings-daemon -# en esta cola—, con la frontera contada símbolo por símbolo en vez de estimada. +# SELLA: b3:0328b0d0. Produce `AccountsService-1.0.typelib`, que es lo que la capa JS del shell pedía. # # CÓMO SE DESTAPÓ, y es la lección de método: gnome-shell selló con su cierre de build COMPLETO # (108/108) y aun así la sesión murió pidiendo este typelib. **El cierre de build no es el cierre de @@ -82,6 +84,7 @@ version = "23.13.9" [source] tarball = "https://www.freedesktop.org/software/accountsservice/accountsservice-23.13.9.tar.xz" sha256 = "adda4cdeae24fa0992e7df3ffff9effa7090be3ac233a3edfdf69d5a9c9b924f" +patches = ["accountsservice-fgetspent_r-musl.patch"] [build] compiler = "zig-cc" diff --git a/recipes/incoming-gnome/libelogind.toml b/recipes/incoming-gnome/libelogind.toml index 50297075..2da61ae3 100644 --- a/recipes/incoming-gnome/libelogind.toml +++ b/recipes/incoming-gnome/libelogind.toml @@ -27,7 +27,14 @@ version = "0.0.1" # versiona el `Cargo.lock` de la raíz: sin él, el vendoreo `--locked` del fetch no es reproducible # (el monorepo lo gitignora en main). repo = "gitea@git.tawasuyu.net:tawasuyu/tawasuyu.git" -commit = "0c9857879ae3a115f9f4c10fe2ea0ab7769763fb" +commit = "b56aeff46a8f707d08188f807aaabeff06faddad" +# El monorepo COMMITEA su propio `vendor/` (`[patch.crates-io] smithay = { path = "vendor/smithay" }`, +# la copia parcheada que mirada necesita para el tearing) y el `cargo vendor` de hammer escribe ahí +# por defecto, pisándolo: el build muere con `failed to read /src/vendor/smithay/.cargo-checksum.json` +# al resolver la dep git de taffy. Se vendorea a un subdir aparte, igual que arje-logind-compat. +# Apareció recién ahora porque el `vendor/` entró a esta rama con el merge de main, no antes. +# Ver [[hammer-cargo-vendor-clobbers-project-vendor]]. +cargo_vendor_dir = ".hammer-cargo-vendor" [build] compiler = "zig-cc" diff --git a/scripts/gnome/hydrate-gnome.sh b/scripts/gnome/hydrate-gnome.sh index b1d5845c..d17f02f8 100755 --- a/scripts/gnome/hydrate-gnome.sh +++ b/scripts/gnome/hydrate-gnome.sh @@ -19,7 +19,17 @@ set -uo pipefail ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT" RFS="${RFS:-work/gnome-rootfs}"; STORE="${STORE:-./store}"; LINK="${LINK:-static}" HAMMER="${HAMMER:-./target/release/hammer}"; KEEP="${KEEP:-0}" -TARGETS=("${@:-recipes/incoming-gnome/gnome-shell.toml}") +# Por defecto se hidratan DOS raíces, no una. accountsservice no es dep de build de gnome-shell —no +# aparece en su meson.build ni en su cierre— sino dep de RUNTIME: la capa JS del shell hace +# `imports.gi.AccountsService` y gjs resuelve el typelib al arrancar. Todo lo que el shell carga por +# `imports.gi.*` es invisible al grafo de deps, así que hay que nombrarlo acá o no entra al rootfs. +# El cierre de BUILD no es el cierre de RUNTIME; ésta es la lista de las raíces que faltan. +# (no `${@:-...}`: con dos valores por defecto y comillas, bash los pasaría como UN solo argumento) +if [ $# -gt 0 ]; then + TARGETS=("$@") +else + TARGETS=(recipes/incoming-gnome/gnome-shell.toml recipes/incoming-gnome/accountsservice.toml) +fi # ── cierre transitivo por el grafo de recetas, en orden topológico ────────────────────────────── PLAN=$(python3 - "${TARGETS[@]}" <<'PY'