[v2,20/23] nss: Convert passwd database to new NSS framework
Checks
| Context |
Check |
Description |
| redhat-pt-bot/TryBot-apply_patch |
success
|
Patch applied to master at the time it was sent
|
| linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-arm |
fail
|
Test failed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 |
fail
|
Test failed
|
Commit Message
This framework moves the ERANGE down a couple of layers. The intent
is to migrate to an interface eventually where NSS service modules
allocate data for the response size they need.
The need for the function-specific GLIBC_2.1.2 versioned symbol
is rather unfortunate. With out the existing preprocessor approach
in nss/getXXbyYY_r.c, this adds a lot of duplicated code. A future
version could use compile-time code generation (outside of the
preprocessor) to generate the _r function wrappers and the
code for the compatibility code. The advantage over the preprocessor
approach is that the resulting code will still be straightforward
to debug because line-based breakpoints work exactly as expected.
---
include/set-freeres.h | 4 +--
malloc/set-freeres.c | 7 +---
nscd/nscd_proto.h | 2 ++
nss/Makefile | 7 ++++
nss/getpwnam.c | 14 ++++----
nss/getpwnam_r.c | 33 +++++++++++++++----
nss/getpwuid.c | 14 ++++----
nss/getpwuid_r.c | 33 +++++++++++++++----
nss/nss_generic.h | 63 ++++++++++++++++++++++++++++++++++++
nss/nss_generic_get.c | 61 +++++++++++++++++++++++++++++++++++
nss/nss_generic_get_r.c | 45 ++++++++++++++++++++++++++
nss/nss_generic_lookup.c | 51 +++++++++++++++++++++++++++++
nss/nss_generic_next.c | 60 +++++++++++++++++++++++++++++++++++
nss/nss_generic_storage.h | 30 ++++++++++++++++++
nss/nss_getX.c | 53 +++++++++++++++++++++++++++++++
nss/nss_getX_r.c | 45 ++++++++++++++++++++++++++
nss/nss_getXinfo.c | 67 +++++++++++++++++++++++++++++++++++++++
17 files changed, 551 insertions(+), 38 deletions(-)
create mode 100644 nss/nss_generic_get.c
create mode 100644 nss/nss_generic_get_r.c
create mode 100644 nss/nss_generic_lookup.c
create mode 100644 nss/nss_generic_next.c
create mode 100644 nss/nss_generic_storage.h
create mode 100644 nss/nss_getX.c
create mode 100644 nss/nss_getX_r.c
create mode 100644 nss/nss_getXinfo.c
Comments
On 3/20/26 4:43 PM, Florian Weimer wrote:
> This framework moves the ERANGE down a couple of layers. The intent
> is to migrate to an interface eventually where NSS service modules
> allocate data for the response size they need.
>
> The need for the function-specific GLIBC_2.1.2 versioned symbol
> is rather unfortunate. With out the existing preprocessor approach
> in nss/getXXbyYY_r.c, this adds a lot of duplicated code. A future
> version could use compile-time code generation (outside of the
> preprocessor) to generate the _r function wrappers and the
> code for the compatibility code. The advantage over the preprocessor
> approach is that the resulting code will still be straightforward
> to debug because line-based breakpoints work exactly as expected.
LGTM.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
> ---
> include/set-freeres.h | 4 +--
> malloc/set-freeres.c | 7 +---
> nscd/nscd_proto.h | 2 ++
> nss/Makefile | 7 ++++
> nss/getpwnam.c | 14 ++++----
> nss/getpwnam_r.c | 33 +++++++++++++++----
> nss/getpwuid.c | 14 ++++----
> nss/getpwuid_r.c | 33 +++++++++++++++----
> nss/nss_generic.h | 63 ++++++++++++++++++++++++++++++++++++
> nss/nss_generic_get.c | 61 +++++++++++++++++++++++++++++++++++
> nss/nss_generic_get_r.c | 45 ++++++++++++++++++++++++++
> nss/nss_generic_lookup.c | 51 +++++++++++++++++++++++++++++
> nss/nss_generic_next.c | 60 +++++++++++++++++++++++++++++++++++
> nss/nss_generic_storage.h | 30 ++++++++++++++++++
> nss/nss_getX.c | 53 +++++++++++++++++++++++++++++++
> nss/nss_getX_r.c | 45 ++++++++++++++++++++++++++
> nss/nss_getXinfo.c | 67 +++++++++++++++++++++++++++++++++++++++
> 17 files changed, 551 insertions(+), 38 deletions(-)
> create mode 100644 nss/nss_generic_get.c
> create mode 100644 nss/nss_generic_get_r.c
> create mode 100644 nss/nss_generic_lookup.c
> create mode 100644 nss/nss_generic_next.c
> create mode 100644 nss/nss_generic_storage.h
> create mode 100644 nss/nss_getX.c
> create mode 100644 nss/nss_getX_r.c
> create mode 100644 nss/nss_getXinfo.c
>
> diff --git a/include/set-freeres.h b/include/set-freeres.h
> index 55093aa89d..c9d2c8ae55 100644
> --- a/include/set-freeres.h
> +++ b/include/set-freeres.h
> @@ -68,6 +68,8 @@ extern void __nss_module_freeres (void) attribute_hidden;
> extern void __nss_action_freeres (void) attribute_hidden;
> /* From nss/nss_database.c */
> extern void __nss_database_freeres (void) attribute_hidden;
> +/* From nss/nss-generic.h. */
> +void __nss_generic_freeres (void) attribute_hidden;
> /* From libio/genops.c */
> extern int _IO_cleanup (void) attribute_hidden;;
> /* From dlfcn/dlerror.c */
> @@ -102,8 +104,6 @@ extern printf_va_arg_function ** __libc_reg_type_freemem_ptr
> /* From nss/getXXbyYY.c */
> extern char * __libc_getgrgid_freemem_ptr attribute_hidden;
> extern char * __libc_getgrnam_freemem_ptr attribute_hidden;
> -extern char * __libc_getpwnam_freemem_ptr attribute_hidden;
> -extern char * __libc_getpwuid_freemem_ptr attribute_hidden;
> extern char * __libc_getspnam_freemem_ptr attribute_hidden;
> extern char * __libc_getaliasbyname_freemem_ptr attribute_hidden;
> extern char * __libc_gethostbyaddr_freemem_ptr attribute_hidden;
> diff --git a/malloc/set-freeres.c b/malloc/set-freeres.c
> index 39fd7d4d27..8112a2f664 100644
> --- a/malloc/set-freeres.c
> +++ b/malloc/set-freeres.c
> @@ -74,8 +74,6 @@
> # pragma weak __libc_reg_type_freemem_ptr
> # pragma weak __libc_getgrgid_freemem_ptr
> # pragma weak __libc_getgrnam_freemem_ptr
> -# pragma weak __libc_getpwnam_freemem_ptr
> -# pragma weak __libc_getpwuid_freemem_ptr
> # pragma weak __libc_getspnam_freemem_ptr
> # pragma weak __libc_getaliasbyname_freemem_ptr
> # pragma weak __libc_gethostbyaddr_freemem_ptr
> @@ -124,6 +122,7 @@ __libc_freeres (void)
> call_function_static_weak (__nss_module_freeres);
> call_function_static_weak (__nss_action_freeres);
> call_function_static_weak (__nss_database_freeres);
> + call_function_static_weak (__nss_generic_freeres);
>
> _IO_cleanup ();
>
> @@ -188,10 +187,6 @@ __libc_freeres (void)
> call_free_static_weak (__libc_reg_printf_freemem_ptr);
> call_free_static_weak (__libc_reg_type_freemem_ptr);
>
> - call_free_static_weak (__libc_getgrgid_freemem_ptr);
> - call_free_static_weak (__libc_getgrnam_freemem_ptr);
> - call_free_static_weak (__libc_getpwnam_freemem_ptr);
> - call_free_static_weak (__libc_getpwuid_freemem_ptr);
> call_free_static_weak (__libc_getspnam_freemem_ptr);
> call_free_static_weak (__libc_getaliasbyname_freemem_ptr);
> call_free_static_weak (__libc_gethostbyaddr_freemem_ptr);
> diff --git a/nscd/nscd_proto.h b/nscd/nscd_proto.h
> index 6c4a318bc0..da3156b0ff 100644
> --- a/nscd/nscd_proto.h
> +++ b/nscd/nscd_proto.h
> @@ -42,9 +42,11 @@ void __nscd_disable_database (unsigned int db) attribute_hidden;
> extern int __nscd_getpwnam_r (const char *name, struct passwd *resultbuf,
> char *buffer, size_t buflen,
> struct passwd **result) attribute_hidden;
> +int __nscd_getpwnam (const char *name, struct passwd **) attribute_hidden;
> extern int __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf,
> char *buffer, size_t buflen,
> struct passwd **result) attribute_hidden;
> +int __nscd_getpwuid (uid_t uid, struct passwd **result) attribute_hidden;
> extern int __nscd_getgrnam_r (const char *name, struct group *resultbuf,
> char *buffer, size_t buflen,
> struct group **result) attribute_hidden;
> diff --git a/nss/Makefile b/nss/Makefile
> index 82f5a5d68f..a13f6fa10c 100644
> --- a/nss/Makefile
> +++ b/nss/Makefile
> @@ -47,7 +47,14 @@ routines = \
> nss_files_functions \
> nss_generic_copy \
> nss_generic_dup \
> + nss_generic_get \
> + nss_generic_get_r \
> + nss_generic_lookup \
> + nss_generic_next \
OK.
> nss_generic_nscd \
> + nss_getX \
> + nss_getX_r \
> + nss_getXinfo \
OK.
> nss_hash \
> nss_module \
> nss_parse_line_result \
> diff --git a/nss/getpwnam.c b/nss/getpwnam.c
> index 36d5a43fd6..55578f7fc0 100644
> --- a/nss/getpwnam.c
> +++ b/nss/getpwnam.c
> @@ -17,12 +17,10 @@
>
> #include <pwd.h>
>
> +#include <nss_generic.h>
>
> -#define LOOKUP_TYPE struct passwd
> -#define FUNCTION_NAME getpwnam
> -#define DATABASE_NAME passwd
> -#define ADD_PARAMS const char *name
> -#define ADD_VARIABLES name
> -#define BUFLEN NSS_BUFLEN_PASSWD
> -
> -#include "../nss/getXXbyYY.c"
> +struct passwd *
> +getpwnam (const char *name)
> +{
> + return __nss_getX (nss_lookup_getpwnam, name);
> +}
> diff --git a/nss/getpwnam_r.c b/nss/getpwnam_r.c
> index 1ba6ba53c9..2bb0bfa5e1 100644
> --- a/nss/getpwnam_r.c
> +++ b/nss/getpwnam_r.c
> @@ -17,12 +17,31 @@
>
> #include <pwd.h>
>
> +#include <nss_generic.h>
> +#include <shlib-compat.h>
>
> -#define LOOKUP_TYPE struct passwd
> -#define FUNCTION_NAME getpwnam
> -#define DATABASE_NAME passwd
> -#define ADD_PARAMS const char *name
> -#define ADD_VARIABLES name
> -#define BUFLEN NSS_BUFLEN_PASSWD
> +int
> +___getpwnam_r (const char *name, struct passwd *pwd,
> + char *buffer, size_t length, struct passwd **result)
> +{
> + void *ptr = pwd;
> + int ret = __nss_getX_r (nss_lookup_getpwnam, name, &ptr, buffer, length);
> + *result = ptr;
> + return ret;
> +}
> +strong_alias (___getpwnam_r, __getpwnam_r)
> +versioned_symbol (libc, ___getpwnam_r, getpwnam_r, GLIBC_2_1_2);
>
> -#include <nss/getXXbyYY_r.c>
> +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1_2)
> +int
> +attribute_compat_text_section
> +__old_getpwnam_r (const char *name, struct passwd *pwd,
> + char *buffer, size_t length, struct passwd **result)
> +{
> + int ret = __getpwnam_r (name, pwd, buffer, length, result);
> + if (ret != 0 || result == NULL)
> + ret = -1;
> + return ret;
> +}
> +compat_symbol (libc, __old_getpwnam_r, getpwnam_r, GLIBC_2_0);
> +#endif
> diff --git a/nss/getpwuid.c b/nss/getpwuid.c
> index 9002dbbc34..96cccdcd89 100644
> --- a/nss/getpwuid.c
> +++ b/nss/getpwuid.c
> @@ -17,12 +17,10 @@
>
> #include <pwd.h>
>
> +#include <nss_generic.h>
>
> -#define LOOKUP_TYPE struct passwd
> -#define FUNCTION_NAME getpwuid
> -#define DATABASE_NAME passwd
> -#define ADD_PARAMS uid_t uid
> -#define ADD_VARIABLES uid
> -#define BUFLEN NSS_BUFLEN_PASSWD
> -
> -#include "../nss/getXXbyYY.c"
> +struct passwd *
> +getpwuid (uid_t uid)
> +{
> + return __nss_getX (nss_lookup_getpwuid, &uid);
> +}
> diff --git a/nss/getpwuid_r.c b/nss/getpwuid_r.c
> index 7c6f3970fa..d99846bb57 100644
> --- a/nss/getpwuid_r.c
> +++ b/nss/getpwuid_r.c
> @@ -17,12 +17,31 @@
>
> #include <pwd.h>
>
> +#include <nss_generic.h>
> +#include <shlib-compat.h>
>
> -#define LOOKUP_TYPE struct passwd
> -#define FUNCTION_NAME getpwuid
> -#define DATABASE_NAME passwd
> -#define ADD_PARAMS uid_t uid
> -#define ADD_VARIABLES uid
> -#define BUFLEN NSS_BUFLEN_PASSWD
> +int
> +___getpwuid_r (uid_t uid, struct passwd *pwd,
> + char *buffer, size_t length, struct passwd **result)
> +{
> + void *ptr = pwd;
> + int ret = __nss_getX_r (nss_lookup_getpwuid, &uid, &ptr, buffer, length);
> + *result = ptr;
> + return ret;
> +}
> +strong_alias (___getpwuid_r, __getpwuid_r)
> +versioned_symbol (libc, ___getpwuid_r, getpwuid_r, GLIBC_2_1_2);
>
> -#include <nss/getXXbyYY_r.c>
> +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1_2)
> +int
> +attribute_compat_text_section
> +__old_getpwuid_r (uid_t uid, struct passwd *pwd,
> + char *buffer, size_t length, struct passwd **result)
> +{
> + int ret = __getpwuid_r (uid, pwd, buffer, length, result);
> + if (ret != 0 || result == NULL)
> + ret = -1;
> + return ret;
> +}
> +compat_symbol (libc, __old_getpwuid_r, getpwuid_r, GLIBC_2_0);
> +#endif
> diff --git a/nss/nss_generic.h b/nss/nss_generic.h
> index 8a5b2cb896..107ebb2003 100644
> --- a/nss/nss_generic.h
> +++ b/nss/nss_generic.h
> @@ -80,4 +80,67 @@ typedef const void *nss_lookup_key;
> bool __nscd_generic_get (enum nss_lookup_type lt, nss_lookup_key key,
> void **result);
>
> +/* Invokes the NSS module function SERVICE_FUNCTION of type LT using
> + KEY. The caller must initialize *RESULT to point to storage of
> + the appropriate NSS struct for LT. Upon return, the function
> + may write NULL to *RESULT and return with an error
> + (including triggering the ERANGE protocol), or leave *RESULT
> + unchanged and fill the struct with data, potentially using
> + LENGTH bytes at BUFFER for additional storage. */
> +enum nss_status __nss_generic_get_r (enum nss_lookup_type lt,
> + nss_lookup_key key,
> + void *service_function,
> + void **result,
> + char *buffer,
> + size_t length) attribute_hidden;
> +
> +/* Like __nss_generic_get_r, but handles the ERANGE protocol.
> + *RESULT is not read, but overwritten with a malloc-allocated
> + pointer or NULL. */
> +enum nss_status __nss_generic_get (enum nss_lookup_type lt,
> + nss_lookup_key key,
> + void *service_function,
> + void **result) attribute_hidden;
> +
> +/* Returns the pointer to the first service lookup function for lookup
> + type LT, or NULL if there are no service modules. Updates *NIP
> + accordingly. */
> +void *__nss_generic_lookup (enum nss_lookup_type lt, nss_action_list *ni)
> + attribute_hidden;
> +
> +/* See __nss_next2 in <nsswitch.h>. The function names are selected
> + based on the type LT. */
> +int __nss_generic_next (enum nss_lookup_type lt, nss_action_list *ni,
> + void *fctp, int status, int all_values)
> + attribute_hidden;
> +
> +/* Perform a group lookup with merging. On success, return zero and
> + write a malloc-allocated struct group pointer to *RESULT (positive
> + result) or NULL (negative result). On failure, return -1 and write
> + NULL to *RESULT, and set errno accordingly. */
> +int __nss_getXinfo (enum nss_lookup_type lt,
> + nss_lookup_key key, void **result) attribute_hidden;
> +
> +/* Like __nss_getXinfo, but LT must be nss_lookup_getgrgid or
> + nss_lookup_getgrnam. This function does not contact nscd and is
> + used in the implementation of __nss_getXInfo. */
> +int __nss_getgrXinfo (enum nss_lookup_type lt,
> + nss_lookup_key key, void **result) attribute_hidden;
> +
> +/* Implementation of the non-_r public interface. RESULT must be the
> + address of the global variable. It is freed before the lookup
> + starts. Returns NULL on failure, and a pointer to the NSS struct
> + appropriate for LT on success. Negative lookup returns NULL and
> + does not set errno. Other errors set errno. */
> +void *__nss_getX (enum nss_lookup_type lt, nss_lookup_key key)
> + attribute_hidden;
> +
> +/* Implementation of the _r public interface. The caller must
> + initialize *RESULT with the address of the result structure passed
> + to the _r function. Return 0 on success and error code on
> + failure (including ERANGE). */
> +int __nss_getX_r (enum nss_lookup_type lt, nss_lookup_key key,
> + void **result, char *buffer, size_t length)
> + attribute_hidden;
> +
> #endif /* NSS_GENERIC_H */
> diff --git a/nss/nss_generic_get.c b/nss/nss_generic_get.c
> new file mode 100644
> index 0000000000..3319a2b9c0
> --- /dev/null
> +++ b/nss/nss_generic_get.c
> @@ -0,0 +1,61 @@
> +/* Implementation of the ERANGE protocol for service module functions.
> + Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <nss_generic.h>
> +#include <nss_generic_storage.h>
> +#include <scratch_buffer.h>
> +
> +enum nss_status
> +__nss_generic_get (enum nss_lookup_type lt,
> + nss_lookup_key key,
> + void *service_function,
> + void **result)
> +{
> + struct scratch_buffer buf;
> + scratch_buffer_init (&buf);
> +
> + *result = NULL;
> + while (true)
> + {
> + union nss_generic_storage storage;
> + void *ptr = &storage;
> + enum nss_status status = __nss_generic_get_r (lt, key,
> + service_function, &ptr,
> + buf.data, buf.length);
> + if (status == NSS_STATUS_TRYAGAIN && errno == ERANGE)
> + {
> + if (!scratch_buffer_grow (&buf))
> + return status;
> + }
> + else
> + {
> + if (status == NSS_STATUS_SUCCESS)
> + {
> + if (ptr != NULL)
> + {
> + ptr = __nss_generic_dup (lt, ptr);
> + if (ptr == NULL)
> + status = NSS_STATUS_TRYAGAIN;
> + *result = ptr;
> + }
> + }
> + scratch_buffer_free (&buf);
> + return status;
> + }
> + }
> +}
> diff --git a/nss/nss_generic_get_r.c b/nss/nss_generic_get_r.c
> new file mode 100644
> index 0000000000..12fedee10f
> --- /dev/null
> +++ b/nss/nss_generic_get_r.c
> @@ -0,0 +1,45 @@
> +/* Lookup-independent call to an NSS service function.
> + Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <nss_generic.h>
> +
> +enum nss_status
> +__nss_generic_get_r (enum nss_lookup_type lt,
> + nss_lookup_key key,
> + void *service_function,
> + void **result,
> + char *buffer,
> + size_t length)
> +{
> + switch (lt)
> + {
> + case nss_lookup_getgrgid:
> + return ((nss_getgrgid_r *) service_function)
> + (*(const gid_t *) key, *result, buffer, length, &errno);
> + case nss_lookup_getgrnam:
> + return ((nss_getgrnam_r *) service_function)
> + (key, *result, buffer, length, &errno);
> + case nss_lookup_getpwnam:
> + return ((nss_getpwnam_r *) service_function)
> + (key, *result, buffer, length, &errno);
> + case nss_lookup_getpwuid:
> + return ((nss_getpwuid_r *) service_function)
> + (*(const uid_t *) key, *result, buffer, length, &errno);
> + }
> + __builtin_unreachable ();
> +}
> diff --git a/nss/nss_generic_lookup.c b/nss/nss_generic_lookup.c
> new file mode 100644
> index 0000000000..f9e41311bc
> --- /dev/null
> +++ b/nss/nss_generic_lookup.c
> @@ -0,0 +1,51 @@
> +/* Type-generic lookup of the first NSS service module function.
> + Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <assert.h>
> +#include <nss_generic.h>
> +
> +void *
> +__nss_generic_lookup (enum nss_lookup_type lt, nss_action_list *ni)
> +{
> + int database;
> + const char *fct_name;
> + const char *fct2_name = NULL;
> +
> + switch (lt)
> + {
> +#define DEFINE_LOOKUP(name, dbname, function, nscddb) \
> + case nss_lookup_##name: \
> + database = nss_database_##dbname; \
> + fct_name = function; \
> + break;
> +#include <nss-lookups.def>
> +#undef DEFINE_LOOKUP
> + default:
> + abort ();
> + }
> +
> + if (! __nss_database_get (database, ni))
> + return NULL;
> +
> + assert (*ni != NULL);
> + void *fct;
> + if (__nss_lookup (ni, fct_name, fct2_name, &fct) == 0)
> + return fct;
> + else
> + return NULL;
> +}
> diff --git a/nss/nss_generic_next.c b/nss/nss_generic_next.c
> new file mode 100644
> index 0000000000..abc5a61e11
> --- /dev/null
> +++ b/nss/nss_generic_next.c
> @@ -0,0 +1,60 @@
> +/* Type-generic next NSS service module function lookup.
> + Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <assert.h>
> +#include <nss_generic.h>
> +
> +int
> +__nss_generic_next (enum nss_lookup_type lt, nss_action_list *ni,
> + void *fctp, int status, int all_values)
> +{
> + /* Extract the database index and function name from <nss-lookups.def>. */
> + int database;
> + const char *fct_name;
> +
> + switch (lt)
> + {
> +#define DEFINE_LOOKUP(name, dbname, function, nscddb) \
> + case nss_lookup_##name: \
> + database = nss_database_##dbname; \
> + fct_name = function; \
> + break;
> +#include <nss-lookups.def>
> +#undef DEFINE_LOOKUP
> + default:
> + abort ();
> + }
> +
> + /* No secondary functions yet. */
> + const char *fct2_name = NULL;
> +
> + int ret;
> + if (*ni == NULL)
> + {
> + /* First call. Ignore status and all_values. */
> + if (! __nss_database_get (database, ni))
> + return -1;
> +
> + assert (*ni != NULL);
> + ret = __nss_lookup (ni, fct_name, fct2_name, fctp);
> + }
> + else
> + ret = __nss_next2 (ni, fct_name, fct2_name, fctp, status, all_values);
> +
> + return ret;
> +}
> diff --git a/nss/nss_generic_storage.h b/nss/nss_generic_storage.h
> new file mode 100644
> index 0000000000..334f51be70
> --- /dev/null
> +++ b/nss/nss_generic_storage.h
> @@ -0,0 +1,30 @@
> +/* Type that can hold all NSS structs.
> + Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef NSS_GENERIC_STORAGE
> +#define NSS_GENERIC_STORAGE
> +
> +#include <grp.h>
> +#include <pwd.h>
> +
> +union nss_generic_storage
> +{
> + struct passwd pwd;
> +};
> +
> +#endif /* NSS_GENERIC_STORAGE */
> diff --git a/nss/nss_getX.c b/nss/nss_getX.c
> new file mode 100644
> index 0000000000..8595e5e3b0
> --- /dev/null
> +++ b/nss/nss_getX.c
> @@ -0,0 +1,53 @@
> +/* Type-generic implementation of public non-_r NSS legacy functions.
> + Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <array_length.h>
> +#include <libc-lock.h>
> +#include <nss_generic.h>
> +#include <set-freeres.h>
> +#include <stdlib.h>
> +
> +static void *__nss_generic_allocation[nss_lookup_MAX];
> +static __libc_lock_t __nss_generic_lock[nss_lookup_MAX];
> +
> +void *
> +__nss_getX (enum nss_lookup_type lt, nss_lookup_key key)
> +{
> + __libc_lock_lock (__nss_generic_lock[lt]);
> + free (__nss_generic_allocation[lt]);
> + __nss_generic_allocation[lt] = NULL;
> +
> + /* Preserve errno (possibly 0) if ret == 0. */
> + int saved_errno = errno;
> + void *result;
> + int ret = __nss_getXinfo (lt, key, &result);
> + __nss_generic_allocation[lt] = result;
> +
> + __libc_lock_unlock (__nss_generic_lock[lt]);
> + if (ret == 0)
> + __set_errno (saved_errno);
> +
> + return result;
> +}
> +
> +void
> +__nss_generic_freeres (void)
> +{
> + for (int i = 0; i < array_length (__nss_generic_allocation); ++i)
> + free (__nss_generic_allocation[i]);
> +}
> diff --git a/nss/nss_getX_r.c b/nss/nss_getX_r.c
> new file mode 100644
> index 0000000000..b0d488c889
> --- /dev/null
> +++ b/nss/nss_getX_r.c
> @@ -0,0 +1,45 @@
> +/* Type-generic implementation of public NSS get*_r functions.
> + Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <errno.h>
> +#include <nss_generic.h>
> +#include <stdlib.h>
> +
> +int
> +__nss_getX_r (enum nss_lookup_type lt, nss_lookup_key key,
> + void **result, char *buffer, size_t length)
> +{
> + void *storage = *result;
> + *result = NULL;
> +
> + void *allocated;
> + int ret = __nss_getXinfo (lt, key, &allocated);
> + if (ret == 0)
> + {
> + if (allocated != NULL)
> + {
> + ret = __nss_generic_copy (lt, allocated, storage, buffer, length);
> + if (ret == 0)
> + *result = storage;
> + free (allocated);
> + }
> + return ret;
> + }
> + else
> + return errno;
> +}
> diff --git a/nss/nss_getXinfo.c b/nss/nss_getXinfo.c
> new file mode 100644
> index 0000000000..0b2588c9fd
> --- /dev/null
> +++ b/nss/nss_getXinfo.c
> @@ -0,0 +1,67 @@
> +/* Generic malloc-compatible version of NSS get functions.
> + Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <nss_generic.h>
> +
> +int
> +__nss_getXinfo (enum nss_lookup_type lt, nss_lookup_key key, void **result)
> +{
> +#ifdef USE_NSCD
> + if (__nscd_generic_get (lt, key, result))
> + return 0;
> +#endif
> +
> + nss_action_list nip = NULL;
> + void *fct;
> + int no_more = __nss_generic_next (lt, &nip, &fct, 0, 0);
> + enum nss_status status = NSS_STATUS_UNAVAIL;
> +
> + *result = NULL;
> + while (no_more == 0)
> + {
> + void *ptr;
> + status = __nss_generic_get (lt, key, fct, &ptr);
> + if (status == NSS_STATUS_SUCCESS)
> + {
> + free (*result);
> + *result = ptr;
> + }
> +
> + /* MERGE is invalid for most databases. */
> + if ((nss_next_action (nip, status) == NSS_ACTION_MERGE
> + && status == NSS_STATUS_SUCCESS))
> + {
> + __set_errno (EINVAL);
> + status = NSS_STATUS_UNAVAIL;
> + break;
> + }
> +
> + no_more = __nss_generic_next (lt, &nip, &fct, status, 0);
> + }
> +
> + if (status != NSS_STATUS_SUCCESS)
> + {
> + free (*result);
> + *result = NULL;
> + }
> +
> + if (status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND)
> + return 0;
> + else
> + return errno;
> +}
@@ -68,6 +68,8 @@ extern void __nss_module_freeres (void) attribute_hidden;
extern void __nss_action_freeres (void) attribute_hidden;
/* From nss/nss_database.c */
extern void __nss_database_freeres (void) attribute_hidden;
+/* From nss/nss-generic.h. */
+void __nss_generic_freeres (void) attribute_hidden;
/* From libio/genops.c */
extern int _IO_cleanup (void) attribute_hidden;;
/* From dlfcn/dlerror.c */
@@ -102,8 +104,6 @@ extern printf_va_arg_function ** __libc_reg_type_freemem_ptr
/* From nss/getXXbyYY.c */
extern char * __libc_getgrgid_freemem_ptr attribute_hidden;
extern char * __libc_getgrnam_freemem_ptr attribute_hidden;
-extern char * __libc_getpwnam_freemem_ptr attribute_hidden;
-extern char * __libc_getpwuid_freemem_ptr attribute_hidden;
extern char * __libc_getspnam_freemem_ptr attribute_hidden;
extern char * __libc_getaliasbyname_freemem_ptr attribute_hidden;
extern char * __libc_gethostbyaddr_freemem_ptr attribute_hidden;
@@ -74,8 +74,6 @@
# pragma weak __libc_reg_type_freemem_ptr
# pragma weak __libc_getgrgid_freemem_ptr
# pragma weak __libc_getgrnam_freemem_ptr
-# pragma weak __libc_getpwnam_freemem_ptr
-# pragma weak __libc_getpwuid_freemem_ptr
# pragma weak __libc_getspnam_freemem_ptr
# pragma weak __libc_getaliasbyname_freemem_ptr
# pragma weak __libc_gethostbyaddr_freemem_ptr
@@ -124,6 +122,7 @@ __libc_freeres (void)
call_function_static_weak (__nss_module_freeres);
call_function_static_weak (__nss_action_freeres);
call_function_static_weak (__nss_database_freeres);
+ call_function_static_weak (__nss_generic_freeres);
_IO_cleanup ();
@@ -188,10 +187,6 @@ __libc_freeres (void)
call_free_static_weak (__libc_reg_printf_freemem_ptr);
call_free_static_weak (__libc_reg_type_freemem_ptr);
- call_free_static_weak (__libc_getgrgid_freemem_ptr);
- call_free_static_weak (__libc_getgrnam_freemem_ptr);
- call_free_static_weak (__libc_getpwnam_freemem_ptr);
- call_free_static_weak (__libc_getpwuid_freemem_ptr);
call_free_static_weak (__libc_getspnam_freemem_ptr);
call_free_static_weak (__libc_getaliasbyname_freemem_ptr);
call_free_static_weak (__libc_gethostbyaddr_freemem_ptr);
@@ -42,9 +42,11 @@ void __nscd_disable_database (unsigned int db) attribute_hidden;
extern int __nscd_getpwnam_r (const char *name, struct passwd *resultbuf,
char *buffer, size_t buflen,
struct passwd **result) attribute_hidden;
+int __nscd_getpwnam (const char *name, struct passwd **) attribute_hidden;
extern int __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf,
char *buffer, size_t buflen,
struct passwd **result) attribute_hidden;
+int __nscd_getpwuid (uid_t uid, struct passwd **result) attribute_hidden;
extern int __nscd_getgrnam_r (const char *name, struct group *resultbuf,
char *buffer, size_t buflen,
struct group **result) attribute_hidden;
@@ -47,7 +47,14 @@ routines = \
nss_files_functions \
nss_generic_copy \
nss_generic_dup \
+ nss_generic_get \
+ nss_generic_get_r \
+ nss_generic_lookup \
+ nss_generic_next \
nss_generic_nscd \
+ nss_getX \
+ nss_getX_r \
+ nss_getXinfo \
nss_hash \
nss_module \
nss_parse_line_result \
@@ -17,12 +17,10 @@
#include <pwd.h>
+#include <nss_generic.h>
-#define LOOKUP_TYPE struct passwd
-#define FUNCTION_NAME getpwnam
-#define DATABASE_NAME passwd
-#define ADD_PARAMS const char *name
-#define ADD_VARIABLES name
-#define BUFLEN NSS_BUFLEN_PASSWD
-
-#include "../nss/getXXbyYY.c"
+struct passwd *
+getpwnam (const char *name)
+{
+ return __nss_getX (nss_lookup_getpwnam, name);
+}
@@ -17,12 +17,31 @@
#include <pwd.h>
+#include <nss_generic.h>
+#include <shlib-compat.h>
-#define LOOKUP_TYPE struct passwd
-#define FUNCTION_NAME getpwnam
-#define DATABASE_NAME passwd
-#define ADD_PARAMS const char *name
-#define ADD_VARIABLES name
-#define BUFLEN NSS_BUFLEN_PASSWD
+int
+___getpwnam_r (const char *name, struct passwd *pwd,
+ char *buffer, size_t length, struct passwd **result)
+{
+ void *ptr = pwd;
+ int ret = __nss_getX_r (nss_lookup_getpwnam, name, &ptr, buffer, length);
+ *result = ptr;
+ return ret;
+}
+strong_alias (___getpwnam_r, __getpwnam_r)
+versioned_symbol (libc, ___getpwnam_r, getpwnam_r, GLIBC_2_1_2);
-#include <nss/getXXbyYY_r.c>
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1_2)
+int
+attribute_compat_text_section
+__old_getpwnam_r (const char *name, struct passwd *pwd,
+ char *buffer, size_t length, struct passwd **result)
+{
+ int ret = __getpwnam_r (name, pwd, buffer, length, result);
+ if (ret != 0 || result == NULL)
+ ret = -1;
+ return ret;
+}
+compat_symbol (libc, __old_getpwnam_r, getpwnam_r, GLIBC_2_0);
+#endif
@@ -17,12 +17,10 @@
#include <pwd.h>
+#include <nss_generic.h>
-#define LOOKUP_TYPE struct passwd
-#define FUNCTION_NAME getpwuid
-#define DATABASE_NAME passwd
-#define ADD_PARAMS uid_t uid
-#define ADD_VARIABLES uid
-#define BUFLEN NSS_BUFLEN_PASSWD
-
-#include "../nss/getXXbyYY.c"
+struct passwd *
+getpwuid (uid_t uid)
+{
+ return __nss_getX (nss_lookup_getpwuid, &uid);
+}
@@ -17,12 +17,31 @@
#include <pwd.h>
+#include <nss_generic.h>
+#include <shlib-compat.h>
-#define LOOKUP_TYPE struct passwd
-#define FUNCTION_NAME getpwuid
-#define DATABASE_NAME passwd
-#define ADD_PARAMS uid_t uid
-#define ADD_VARIABLES uid
-#define BUFLEN NSS_BUFLEN_PASSWD
+int
+___getpwuid_r (uid_t uid, struct passwd *pwd,
+ char *buffer, size_t length, struct passwd **result)
+{
+ void *ptr = pwd;
+ int ret = __nss_getX_r (nss_lookup_getpwuid, &uid, &ptr, buffer, length);
+ *result = ptr;
+ return ret;
+}
+strong_alias (___getpwuid_r, __getpwuid_r)
+versioned_symbol (libc, ___getpwuid_r, getpwuid_r, GLIBC_2_1_2);
-#include <nss/getXXbyYY_r.c>
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1_2)
+int
+attribute_compat_text_section
+__old_getpwuid_r (uid_t uid, struct passwd *pwd,
+ char *buffer, size_t length, struct passwd **result)
+{
+ int ret = __getpwuid_r (uid, pwd, buffer, length, result);
+ if (ret != 0 || result == NULL)
+ ret = -1;
+ return ret;
+}
+compat_symbol (libc, __old_getpwuid_r, getpwuid_r, GLIBC_2_0);
+#endif
@@ -80,4 +80,67 @@ typedef const void *nss_lookup_key;
bool __nscd_generic_get (enum nss_lookup_type lt, nss_lookup_key key,
void **result);
+/* Invokes the NSS module function SERVICE_FUNCTION of type LT using
+ KEY. The caller must initialize *RESULT to point to storage of
+ the appropriate NSS struct for LT. Upon return, the function
+ may write NULL to *RESULT and return with an error
+ (including triggering the ERANGE protocol), or leave *RESULT
+ unchanged and fill the struct with data, potentially using
+ LENGTH bytes at BUFFER for additional storage. */
+enum nss_status __nss_generic_get_r (enum nss_lookup_type lt,
+ nss_lookup_key key,
+ void *service_function,
+ void **result,
+ char *buffer,
+ size_t length) attribute_hidden;
+
+/* Like __nss_generic_get_r, but handles the ERANGE protocol.
+ *RESULT is not read, but overwritten with a malloc-allocated
+ pointer or NULL. */
+enum nss_status __nss_generic_get (enum nss_lookup_type lt,
+ nss_lookup_key key,
+ void *service_function,
+ void **result) attribute_hidden;
+
+/* Returns the pointer to the first service lookup function for lookup
+ type LT, or NULL if there are no service modules. Updates *NIP
+ accordingly. */
+void *__nss_generic_lookup (enum nss_lookup_type lt, nss_action_list *ni)
+ attribute_hidden;
+
+/* See __nss_next2 in <nsswitch.h>. The function names are selected
+ based on the type LT. */
+int __nss_generic_next (enum nss_lookup_type lt, nss_action_list *ni,
+ void *fctp, int status, int all_values)
+ attribute_hidden;
+
+/* Perform a group lookup with merging. On success, return zero and
+ write a malloc-allocated struct group pointer to *RESULT (positive
+ result) or NULL (negative result). On failure, return -1 and write
+ NULL to *RESULT, and set errno accordingly. */
+int __nss_getXinfo (enum nss_lookup_type lt,
+ nss_lookup_key key, void **result) attribute_hidden;
+
+/* Like __nss_getXinfo, but LT must be nss_lookup_getgrgid or
+ nss_lookup_getgrnam. This function does not contact nscd and is
+ used in the implementation of __nss_getXInfo. */
+int __nss_getgrXinfo (enum nss_lookup_type lt,
+ nss_lookup_key key, void **result) attribute_hidden;
+
+/* Implementation of the non-_r public interface. RESULT must be the
+ address of the global variable. It is freed before the lookup
+ starts. Returns NULL on failure, and a pointer to the NSS struct
+ appropriate for LT on success. Negative lookup returns NULL and
+ does not set errno. Other errors set errno. */
+void *__nss_getX (enum nss_lookup_type lt, nss_lookup_key key)
+ attribute_hidden;
+
+/* Implementation of the _r public interface. The caller must
+ initialize *RESULT with the address of the result structure passed
+ to the _r function. Return 0 on success and error code on
+ failure (including ERANGE). */
+int __nss_getX_r (enum nss_lookup_type lt, nss_lookup_key key,
+ void **result, char *buffer, size_t length)
+ attribute_hidden;
+
#endif /* NSS_GENERIC_H */
new file mode 100644
@@ -0,0 +1,61 @@
+/* Implementation of the ERANGE protocol for service module functions.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <nss_generic.h>
+#include <nss_generic_storage.h>
+#include <scratch_buffer.h>
+
+enum nss_status
+__nss_generic_get (enum nss_lookup_type lt,
+ nss_lookup_key key,
+ void *service_function,
+ void **result)
+{
+ struct scratch_buffer buf;
+ scratch_buffer_init (&buf);
+
+ *result = NULL;
+ while (true)
+ {
+ union nss_generic_storage storage;
+ void *ptr = &storage;
+ enum nss_status status = __nss_generic_get_r (lt, key,
+ service_function, &ptr,
+ buf.data, buf.length);
+ if (status == NSS_STATUS_TRYAGAIN && errno == ERANGE)
+ {
+ if (!scratch_buffer_grow (&buf))
+ return status;
+ }
+ else
+ {
+ if (status == NSS_STATUS_SUCCESS)
+ {
+ if (ptr != NULL)
+ {
+ ptr = __nss_generic_dup (lt, ptr);
+ if (ptr == NULL)
+ status = NSS_STATUS_TRYAGAIN;
+ *result = ptr;
+ }
+ }
+ scratch_buffer_free (&buf);
+ return status;
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,45 @@
+/* Lookup-independent call to an NSS service function.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <nss_generic.h>
+
+enum nss_status
+__nss_generic_get_r (enum nss_lookup_type lt,
+ nss_lookup_key key,
+ void *service_function,
+ void **result,
+ char *buffer,
+ size_t length)
+{
+ switch (lt)
+ {
+ case nss_lookup_getgrgid:
+ return ((nss_getgrgid_r *) service_function)
+ (*(const gid_t *) key, *result, buffer, length, &errno);
+ case nss_lookup_getgrnam:
+ return ((nss_getgrnam_r *) service_function)
+ (key, *result, buffer, length, &errno);
+ case nss_lookup_getpwnam:
+ return ((nss_getpwnam_r *) service_function)
+ (key, *result, buffer, length, &errno);
+ case nss_lookup_getpwuid:
+ return ((nss_getpwuid_r *) service_function)
+ (*(const uid_t *) key, *result, buffer, length, &errno);
+ }
+ __builtin_unreachable ();
+}
new file mode 100644
@@ -0,0 +1,51 @@
+/* Type-generic lookup of the first NSS service module function.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <assert.h>
+#include <nss_generic.h>
+
+void *
+__nss_generic_lookup (enum nss_lookup_type lt, nss_action_list *ni)
+{
+ int database;
+ const char *fct_name;
+ const char *fct2_name = NULL;
+
+ switch (lt)
+ {
+#define DEFINE_LOOKUP(name, dbname, function, nscddb) \
+ case nss_lookup_##name: \
+ database = nss_database_##dbname; \
+ fct_name = function; \
+ break;
+#include <nss-lookups.def>
+#undef DEFINE_LOOKUP
+ default:
+ abort ();
+ }
+
+ if (! __nss_database_get (database, ni))
+ return NULL;
+
+ assert (*ni != NULL);
+ void *fct;
+ if (__nss_lookup (ni, fct_name, fct2_name, &fct) == 0)
+ return fct;
+ else
+ return NULL;
+}
new file mode 100644
@@ -0,0 +1,60 @@
+/* Type-generic next NSS service module function lookup.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <assert.h>
+#include <nss_generic.h>
+
+int
+__nss_generic_next (enum nss_lookup_type lt, nss_action_list *ni,
+ void *fctp, int status, int all_values)
+{
+ /* Extract the database index and function name from <nss-lookups.def>. */
+ int database;
+ const char *fct_name;
+
+ switch (lt)
+ {
+#define DEFINE_LOOKUP(name, dbname, function, nscddb) \
+ case nss_lookup_##name: \
+ database = nss_database_##dbname; \
+ fct_name = function; \
+ break;
+#include <nss-lookups.def>
+#undef DEFINE_LOOKUP
+ default:
+ abort ();
+ }
+
+ /* No secondary functions yet. */
+ const char *fct2_name = NULL;
+
+ int ret;
+ if (*ni == NULL)
+ {
+ /* First call. Ignore status and all_values. */
+ if (! __nss_database_get (database, ni))
+ return -1;
+
+ assert (*ni != NULL);
+ ret = __nss_lookup (ni, fct_name, fct2_name, fctp);
+ }
+ else
+ ret = __nss_next2 (ni, fct_name, fct2_name, fctp, status, all_values);
+
+ return ret;
+}
new file mode 100644
@@ -0,0 +1,30 @@
+/* Type that can hold all NSS structs.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef NSS_GENERIC_STORAGE
+#define NSS_GENERIC_STORAGE
+
+#include <grp.h>
+#include <pwd.h>
+
+union nss_generic_storage
+{
+ struct passwd pwd;
+};
+
+#endif /* NSS_GENERIC_STORAGE */
new file mode 100644
@@ -0,0 +1,53 @@
+/* Type-generic implementation of public non-_r NSS legacy functions.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <libc-lock.h>
+#include <nss_generic.h>
+#include <set-freeres.h>
+#include <stdlib.h>
+
+static void *__nss_generic_allocation[nss_lookup_MAX];
+static __libc_lock_t __nss_generic_lock[nss_lookup_MAX];
+
+void *
+__nss_getX (enum nss_lookup_type lt, nss_lookup_key key)
+{
+ __libc_lock_lock (__nss_generic_lock[lt]);
+ free (__nss_generic_allocation[lt]);
+ __nss_generic_allocation[lt] = NULL;
+
+ /* Preserve errno (possibly 0) if ret == 0. */
+ int saved_errno = errno;
+ void *result;
+ int ret = __nss_getXinfo (lt, key, &result);
+ __nss_generic_allocation[lt] = result;
+
+ __libc_lock_unlock (__nss_generic_lock[lt]);
+ if (ret == 0)
+ __set_errno (saved_errno);
+
+ return result;
+}
+
+void
+__nss_generic_freeres (void)
+{
+ for (int i = 0; i < array_length (__nss_generic_allocation); ++i)
+ free (__nss_generic_allocation[i]);
+}
new file mode 100644
@@ -0,0 +1,45 @@
+/* Type-generic implementation of public NSS get*_r functions.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <nss_generic.h>
+#include <stdlib.h>
+
+int
+__nss_getX_r (enum nss_lookup_type lt, nss_lookup_key key,
+ void **result, char *buffer, size_t length)
+{
+ void *storage = *result;
+ *result = NULL;
+
+ void *allocated;
+ int ret = __nss_getXinfo (lt, key, &allocated);
+ if (ret == 0)
+ {
+ if (allocated != NULL)
+ {
+ ret = __nss_generic_copy (lt, allocated, storage, buffer, length);
+ if (ret == 0)
+ *result = storage;
+ free (allocated);
+ }
+ return ret;
+ }
+ else
+ return errno;
+}
new file mode 100644
@@ -0,0 +1,67 @@
+/* Generic malloc-compatible version of NSS get functions.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <nss_generic.h>
+
+int
+__nss_getXinfo (enum nss_lookup_type lt, nss_lookup_key key, void **result)
+{
+#ifdef USE_NSCD
+ if (__nscd_generic_get (lt, key, result))
+ return 0;
+#endif
+
+ nss_action_list nip = NULL;
+ void *fct;
+ int no_more = __nss_generic_next (lt, &nip, &fct, 0, 0);
+ enum nss_status status = NSS_STATUS_UNAVAIL;
+
+ *result = NULL;
+ while (no_more == 0)
+ {
+ void *ptr;
+ status = __nss_generic_get (lt, key, fct, &ptr);
+ if (status == NSS_STATUS_SUCCESS)
+ {
+ free (*result);
+ *result = ptr;
+ }
+
+ /* MERGE is invalid for most databases. */
+ if ((nss_next_action (nip, status) == NSS_ACTION_MERGE
+ && status == NSS_STATUS_SUCCESS))
+ {
+ __set_errno (EINVAL);
+ status = NSS_STATUS_UNAVAIL;
+ break;
+ }
+
+ no_more = __nss_generic_next (lt, &nip, &fct, status, 0);
+ }
+
+ if (status != NSS_STATUS_SUCCESS)
+ {
+ free (*result);
+ *result = NULL;
+ }
+
+ if (status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND)
+ return 0;
+ else
+ return errno;
+}