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; +}