tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1181Shell 82Rust 62Markdown 521515 Definiciones

gnome: accountsservice SELLA (b3:0328b0d0) — cae la frontera de la C-ABI de logind

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) <noreply@anthropic.com>

08fc1d4b92d5f6391fcbcdf361f6fa068af6fdad · por sergio · (hace 2 meses) · padre b29bdbb34 · ver el árbol en este commit

Delta semántico

Ninguna definición cambió: el cambio es de texto, documentación o datos.

4 archivos · +140 −17

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'