Subject: Dynamic module support for nsswitch
To: NetBSD Userlevel Technical Discussion List <tech-userlevel@netbsd.org>
From: Jason Thorpe <thorpej@wasabisystems.com>
List: tech-userlevel
Date: 07/18/2004 16:39:05
--Apple-Mail-23-217596080
Content-Type: multipart/mixed; boundary=Apple-Mail-22-217596071
--Apple-Mail-22-217596071
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
format=flowed
Hi folks...
I have a need for dynamic nsswitch module support, so I adapted the
work done by FreeBSD in this area to our libc. I reworked the code
significantly, but it maintains FreeBSD API compatibility with the
module support[*], and API / ABI compatibility with extant NetBSD
nsswitch support.
[*] FreeBSD uses the _r versions of getpwnam(), etc. for their module
methods, which we do not have, but this is really a minor point that I
understand Brian Ginsbach is working on. Brian -- I hope that your
work will allow us to have API compatibility with FreeBSD nsswitch
modules in this regard. Brian, once your work is done, I'd like to
also bring in FreeBSD's glibc NSS module compatibility shim.
Attached is the patch that adds this dynamic module support. I'd like
to check it in ASAP so that I can start work on making Samba's winbind
NSS module work with NetBSD.
-- Jason R. Thorpe <thorpej@wasabisystems.com>
--Apple-Mail-22-217596071
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
x-unix-mode=0644;
name="nsswitch-patch.txt"
Content-Disposition: attachment;
filename=nsswitch-patch.txt
Index: include/nsswitch.h
===================================================================
RCS file: /cvsroot/src/include/nsswitch.h,v
retrieving revision 1.12
diff -c -p -r1.12 nsswitch.h
*** include/nsswitch.h 9 Jul 2003 01:59:34 -0000 1.12
--- include/nsswitch.h 18 Jul 2004 23:29:06 -0000
***************
*** 49,54 ****
--- 49,56 ----
#include <machine/ansi.h>
#include <sys/types.h>
+ #define NSS_MODULE_INTERFACE_VERSION 0
+
#ifndef _PATH_NS_CONF
#define _PATH_NS_CONF "/etc/nsswitch.conf"
#endif
***************
*** 103,114 ****
#define NSDB_TTYS "ttys"
/*
* ns_dtab - `nsswitch dispatch table'
* contains an entry for each source and the appropriate function to call
*/
typedef struct {
const char *src;
! int (*callback) __P((void *, void *, _BSD_VA_LIST_));
void *cb_data;
} ns_dtab;
--- 105,121 ----
#define NSDB_TTYS "ttys"
/*
+ * ns_dtab `callback' function signature.
+ */
+ typedef int (*nss_method)(void *, void *, _BSD_VA_LIST_);
+
+ /*
* ns_dtab - `nsswitch dispatch table'
* contains an entry for each source and the appropriate function to call
*/
typedef struct {
const char *src;
! nss_method callback;
void *cb_data;
} ns_dtab;
*************** typedef struct {
*** 148,153 ****
--- 155,183 ----
extern const ns_src __nsdefaultsrc[];
+ /*
+ * ns_mtab - `nsswitch method table'
+ * An nsswitch module provides a mapping from (database name, method name)
+ * tuples to the nss_method and associated callback data. Effectively,
+ * ns_dtab, but used for dynamically loaded modules.
+ */
+ typedef struct {
+ const char *database;
+ const char *name;
+ nss_method method;
+ void *mdata;
+ } ns_mtab;
+
+ /*
+ * nss_module_register_fn - module registration function
+ * called at module load
+ * nss_module_unregister_fn - module un-registration function
+ * called at module unload
+ */
+ typedef void (*nss_module_unregister_fn)(ns_mtab *, u_int);
+ typedef ns_mtab *(*nss_module_register_fn)(const char *, u_int *,
+ nss_module_unregister_fn *);
+
#ifdef _NS_PRIVATE
/*
*************** typedef struct {
*** 165,170 ****
--- 195,212 ----
int srclistsize; /* size of srclist */
} ns_dbt;
+ /*
+ * ns_mod - `nsswitch module'
+ */
+ typedef struct {
+ const char *name; /* module name */
+ void *handle; /* handle from dlopen() */
+ ns_mtab *mtab; /* method table */
+ u_int mtabsize; /* size of mtab */
+ /* called to unload module */
+ nss_module_unregister_fn unregister;
+ } ns_mod;
+
#endif /* _NS_PRIVATE */
*************** int nsdispatch __P((void *, const ns_dta
*** 177,183 ****
#ifdef _NS_PRIVATE
int _nsdbtaddsrc __P((ns_dbt *, const ns_src *));
void _nsdbtdump __P((const ns_dbt *));
- const ns_dbt *_nsdbtget __P((const char *));
int _nsdbtput __P((const ns_dbt *));
void _nsyyerror __P((const char *));
int _nsyylex __P((void));
--- 219,224 ----
Index: lib/libc/net/nsdispatch.3
===================================================================
RCS file: /cvsroot/src/lib/libc/net/nsdispatch.3,v
retrieving revision 1.15
diff -c -p -r1.15 nsdispatch.3
*** lib/libc/net/nsdispatch.3 26 Jul 2003 19:24:48 -0000 1.15
--- lib/libc/net/nsdispatch.3 18 Jul 2004 23:29:06 -0000
***************
*** 1,10 ****
.\" $NetBSD: nsdispatch.3,v 1.15 2003/07/26 19:24:48 salo Exp $
.\"
! .\" Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc.
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to The NetBSD Foundation
! .\" by Luke Mewburn.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
--- 1,10 ----
.\" $NetBSD: nsdispatch.3,v 1.15 2003/07/26 19:24:48 salo Exp $
.\"
! .\" Copyright (c) 1997, 1998, 1999, 2004 The NetBSD Foundation, Inc.
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to The NetBSD Foundation
! .\" by Luke Mewburn; and by Jason R. Thorpe.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
***************
*** 34,40 ****
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
! .Dd January 19, 1999
.Dt NSDISPATCH 3
.Os
.Sh NAME
--- 34,40 ----
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
! .Dd July 18, 2004
.Dt NSDISPATCH 3
.Os
.Sh NAME
*************** until a successful entry is found.
*** 67,73 ****
.Va retval
is passed to each callback function to modify as necessary
(to pass back to the caller of
! .Fn nsdispatch )
.Pp
.Va dtab
is an array of
--- 67,78 ----
.Va retval
is passed to each callback function to modify as necessary
(to pass back to the caller of
! .Fn nsdispatch ) .
! .Pp
! Each callback has the function signature described by the typedef:
! .Pp
! .Ft typedef int
! .Fn \*(lp*nss_method\*(rp "void *retval" "void *cb_data" "va_list *ap" ;
.Pp
.Va dtab
is an array of
*************** structures, which have the following for
*** 76,94 ****
.Bd -literal -offset indent
typedef struct {
const char *src;
! int (*cb)(void *retval, void *cb_data, va_list ap);
void *cb_data;
} ns_dtab;
.Ed
.Pp
.Bd -ragged -offset indent
! For each source type that is implemented, an entry with
.Va src
! set to the name of the source,
.Va cb
! defined as a function which handles that source, and
.Va cb_data
! is used to pass arbitrary data to the callback function.
The last entry in
.Va dtab
should contain
--- 81,102 ----
.Bd -literal -offset indent
typedef struct {
const char *src;
! nss_method cb;
void *cb_data;
} ns_dtab;
.Ed
.Pp
.Bd -ragged -offset indent
! The
! .Fa dtab
! array should consist of one entry for each source type that is implemented,
! with
.Va src
! as the name of the source,
.Va cb
! as a function which handles that source, and
.Va cb_data
! as a pointer to arbitrary data to be passed to the callback.
The last entry in
.Va dtab
should contain
*************** and
*** 100,113 ****
.Va cb_data .
.Ed
.Pp
! .Va method
is usually the name of the function calling
.Fn nsdispatch .
! When dynamic loading is supported, a symbol constructed from
! .Va database ,
! the current source, and
! .Va method
! will be used as the name to invoke the dynamically loaded function.
.Pp
.Va defaults
contains a list of default sources to try in the case of
--- 108,126 ----
.Va cb_data .
.Ed
.Pp
! Callbacks may also be provided by dynamically-loaded modules, in which
! case they are selected using the
! .Fa database
! and
! .Fa method
! arguments in addition to the configured source.
! .Fa method
is usually the name of the function calling
.Fn nsdispatch .
! Note that the callbacks provided by
! .Fa dtab
! take priority over those implemented in dynamically-loaded modules in the
! event of a conflict.
.Pp
.Va defaults
contains a list of default sources to try in the case of
*************** typedef struct {
*** 126,143 ****
.Ed
.Pp
.Bd -ragged -offset indent
! For each default source type, an entry with
! .Va src
! set to the name of the source, and
! .Va flags
! set to the relevant flags
(usually
.Dv NS_SUCCESS ;
refer to
.Sx Callback return values
for more information).
The last entry in
! .Va defaults
should have
.Va src
set to
--- 139,159 ----
.Ed
.Pp
.Bd -ragged -offset indent
! The
! .Fa defaults
! array should consist of one entry foe each source to consult by default
! indicated by
! .Va src ,
! and
! .Fa flags
! set to the desired behavior
(usually
.Dv NS_SUCCESS ;
refer to
.Sx Callback return values
for more information).
The last entry in
! .Fa defaults
should have
.Va src
set to
*************** are optional extra arguments, which
*** 158,167 ****
are passed to the appropriate callback function as a variable argument
list of the type
.Va va_list .
.Ss Valid source types
! Whilst there is support for arbitrary sources, the following
! #defines for commonly implemented sources are available:
! .Bl -column NS_COMPAT COMPAT -offset indent
.Sy #define value
.It NSSRC_FILES "files"
.It NSSRC_DNS "dns"
--- 174,261 ----
are passed to the appropriate callback function as a variable argument
list of the type
.Va va_list .
+ .Ss Dynamically-loaded module interface
+ The
+ .Fn nsdispatch
+ function loads callback modules from the run-time link-editor's search
+ path using the following naming convention:
+ .Bd -literal -offset indent
+ nss_<source>.so.<version>
+ .Ed
+ .Bl -tag -width <version> -offset indent
+ .It <source>
+ The source that the module implements.
+ .It <version>
+ The
+ .Nm nsdispatch
+ module interface version, which is defined by the integer
+ .Dv NSS_MODULE_INTERFACE_VERSION ,
+ which has the value 0.
+ .El
+ .Pp
+ When a module is loaded,
+ .Fn nsdispatch
+ looks for and calls the following function in the module:
+ .Pp
+ .Ft ns_mtab *
+ .Fn nss_module_register "const char *source" "u_int *nelems" \
+ "nss_module_unregister_fn *unreg" ;
+ .Pp
+ .Bl -tag -width source
+ .It Fa source
+ The name of the source that the module implements, as used by
+ .Fn nsdispatch
+ to construct the module's name.
+ .It Fa nelems
+ A pointer to an unsigned integer that
+ .Fn nss_module_register
+ should set to the number of elements in the
+ .Va ns_mtab
+ array returned by
+ .Fn nss_module_register .
+ .It Fa unreg
+ A pointer to a function pointer that
+ .Fn nss_module_resgister
+ can optionally set to a function to be invoked when the module is
+ unloaded.
+ .El
+ .Pp
+ The unregister function signature is described by the typedef:
+ .Pp
+ .Ft typedef void
+ .Fn \*(lp*nss_module_unregister_fn\*(rp "ns_mtab *mtab" "u_int nelems" ;
+ .Pp
+ .Fn nss_module_register
+ returns an array of
+ .Va ns_mtab
+ structures, which have the following format:
+ .Bd -literal -offset indent
+ typedef struct {
+ const char *database;
+ const char *name;
+ nss_method method;
+ void *mdata;
+ } ns_mtab;
+ .Ed
+ .Pp
+ .Bd -ragged -offset indent
+ The
+ .Fa mtab
+ array should consist of one entry for each method that is implemented,
+ with
+ .Va database
+ as the name of the database,
+ .Va name
+ as the name of the method,
+ .Va method
+ as the callback that implements the method, and
+ .Va mdata
+ as a pointer to arbitrary data to be passed to the callback.
+ .Ed
.Ss Valid source types
! While there is support for arbitrary sources, the following
! #defines for commonly implemented sources are provided:
! .Bl -column NSSRC_COMPAT COMPAT -offset indent
.Sy #define value
.It NSSRC_FILES "files"
.It NSSRC_DNS "dns"
*************** Whilst there is support for arbitrary so
*** 172,177 ****
--- 266,287 ----
Refer to
.Xr nsswitch.conf 5
for a complete description of what each source type is.
+ .Ss Valid database types
+ While there is support for arbitrary databases, the following
+ #defines for currently implemented system databases are provided:
+ .Bl -column NSDB_NETGROUP NETGROUP -offset indent
+ .Sy #define value
+ .It NSDB_HOSTS "hosts"
+ .It NSDB_GROUP "group"
+ .It NSDB_NETGROUP "netgroup"
+ .It NSDB_NETWORKS "networks"
+ .It NSDB_PASSWD "passwd"
+ .It NSDB_SHELLS "shells"
+ .El
+ .Pp
+ Refer to
+ .Xr nsswitch.conf 5
+ for a complete description of what each database is.
.Ss Callback return values
The callback functions should return one of the following values
depending upon status of the lookup:
*************** returns the value of the callback that c
*** 192,197 ****
--- 302,308 ----
or NS_NOTFOUND otherwise.
.Sh SEE ALSO
.Xr hesiod 3 ,
+ .Xr ld.elf_so 1 ,
.Xr stdarg 3 ,
.Xr ypclnt 3 ,
.Xr nsswitch.conf 5
*************** The
*** 200,205 ****
--- 311,318 ----
.Nm
routines first appeared in
.Nx 1.4 .
+ Support for dynamically-loaded modules first appeared in
+ .Nx 3.0 .
.Sh AUTHORS
Luke Mewburn
.Aq lukem@NetBSD.org
*************** and
*** 211,224 ****
.Tn Solaris
.Xr nsswitch.conf 4
manual pages.
! .Sh BUGS
! The
! .Nm
! routines are not thread safe.
! This will be rectified in the future.
! .Pp
! Currently there is no support for dynamically loadable dispatcher callback
! functions.
! It is anticipated that this will be added in the future in the back-end
! without requiring changes to code that invokes
! .Fn nsdispatch .
--- 324,331 ----
.Tn Solaris
.Xr nsswitch.conf 4
manual pages.
! Support for dynamically-loaded modules was added by Jason Thorpe
! .Aq thorpej@NetBSD.org ,
! based on code developed by the
! .Fx
! Project.
Index: lib/libc/net/nsdispatch.c
===================================================================
RCS file: /cvsroot/src/lib/libc/net/nsdispatch.c,v
retrieving revision 1.21
diff -c -p -r1.21 nsdispatch.c
*** lib/libc/net/nsdispatch.c 16 Jul 2004 16:11:43 -0000 1.21
--- lib/libc/net/nsdispatch.c 18 Jul 2004 23:29:07 -0000
***************
*** 1,11 ****
/* $NetBSD: nsdispatch.c,v 1.21 2004/07/16 16:11:43 thorpej Exp $ */
/*-
! * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
! * by Luke Mewburn.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
--- 1,11 ----
/* $NetBSD: nsdispatch.c,v 1.21 2004/07/16 16:11:43 thorpej Exp $ */
/*-
! * Copyright (c) 1997, 1998, 1999, 2004 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
! * by Luke Mewburn; and by Jason R. Thorpe.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
***************
*** 36,41 ****
--- 36,73 ----
* POSSIBILITY OF SUCH DAMAGE.
*/
+ /*-
+ * Copyright (c) 2003 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * Jacques A. Vidrine, Safeport Network Services, and Network
+ * Associates Laboratories, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD: nsdispatch.c,v 1.21 2004/07/16 16:11:43 thorpej Exp $");
*************** __RCSID("$NetBSD: nsdispatch.c,v 1.21 20
*** 48,53 ****
--- 80,88 ----
#include <sys/stat.h>
#include <assert.h>
+ #ifdef __ELF__
+ #include <dlfcn.h>
+ #endif /* __ELF__ */
#include <err.h>
#include <fcntl.h>
#define _NS_PRIVATE
*************** const ns_src __nsdefaultsrc[] = {
*** 76,133 ****
{ 0 },
};
- static int _nsmapsize = 0;
- static ns_dbt *_nsmap = NULL;
#ifdef _REENTRANT
! static mutex_t _nsmutex = MUTEX_INITIALIZER;
! #define NSLOCK() mutex_lock(&_nsmutex)
! #define NSUNLOCK() mutex_unlock(&_nsmutex)
! #else
! #define NSLOCK()
! #define NSUNLOCK()
#endif
/*
! * size of dynamic array chunk for _nsmap and _nsmap[x].srclist
*/
#define NSELEMSPERCHUNK 8
static int
! _nscmp(const void *a, const void *b)
{
return (strcasecmp(((const ns_dbt *)a)->name,
((const ns_dbt *)b)->name));
}
int
_nsdbtaddsrc(ns_dbt *dbt, const ns_src *src)
{
_DIAGASSERT(dbt != NULL);
_DIAGASSERT(src != NULL);
! if ((dbt->srclistsize % NSELEMSPERCHUNK) == 0) {
! ns_src *new;
- new = (ns_src *)realloc(dbt->srclist,
- (dbt->srclistsize + NSELEMSPERCHUNK) * sizeof(ns_src));
- if (new == NULL)
- return (-1);
- dbt->srclist = new;
- }
- memmove(&dbt->srclist[dbt->srclistsize++], src, sizeof(ns_src));
return (0);
}
-
void
_nsdbtdump(const ns_dbt *dbt)
{
! int i;
_DIAGASSERT(dbt != NULL);
--- 111,368 ----
{ 0 },
};
+ /* Database, source mappings. */
+ static u_int _nsmapsize;
+ static ns_dbt *_nsmap;
+
+ /* Nsswitch modules. */
+ static u_int _nsmodsize;
+ static ns_mod *_nsmod;
+
+ /* Placeholder for built-in modules' dlopen() handles. */
+ static void *_nsbuiltin = &_nsbuiltin;
#ifdef _REENTRANT
! /*
! * Global nsswitch data structures are mostly read-only, but we update them
! * when we read or re-read nsswitch.conf.
! */
! static rwlock_t _nslock = RWLOCK_INITIALIZER;
#endif
/*
! * Runtime determination of whether we are dynamically linked or not.
! */
! #ifdef __ELF__
! extern int _DYNAMIC __attribute__((__weak__));
! #define is_dynamic() (&_DYNAMIC != NULL)
! #else
! #define is_dynamic() (0) /* don't bother - switch to ELF! */
! #endif /* __ELF__ */
!
!
! /*
! * size of dynamic array chunk for _nsmap and _nsmap[x].srclist (and other
! * growing arrays).
*/
#define NSELEMSPERCHUNK 8
+ /*
+ * Dynamically growable arrays are used for lists of databases, sources,
+ * and modules. The following "vector" API is used to isolate the
+ * common operations.
+ */
+ typedef void (*_nsvect_free_elem)(void *);
+
+ static void *
+ _nsvect_append(const void *elem, void *vec, u_int *count, size_t esize)
+ {
+ void *p;
+
+ if ((*count % NSELEMSPERCHUNK) == 0) {
+ p = realloc(vec, (*count + NSELEMSPERCHUNK) * esize);
+ if (p == NULL)
+ return (NULL);
+ vec = p;
+ }
+ memmove((void *)(((uintptr_t)vec) + (*count * esize)), elem, esize);
+ (*count)++;
+ return (vec);
+ }
+
+ static void *
+ _nsvect_elem(u_int i, void *vec, u_int count, size_t esize)
+ {
+
+ if (i < count)
+ return ((void *)((uintptr_t)vec + (i * esize)));
+ else
+ return (NULL);
+ }
+
+ static void
+ _nsvect_free(void *vec, u_int *count, size_t esize, _nsvect_free_elem free_elem)
+ {
+ void *elem;
+ u_int i;
+
+ for (i = 0; i < *count; i++) {
+ elem = _nsvect_elem(i, vec, *count, esize);
+ if (elem != NULL)
+ (*free_elem)(elem);
+ }
+ if (vec != NULL)
+ free(vec);
+ *count = 0;
+ }
+ #define _NSVECT_FREE(v, c, s, f) \
+ do { \
+ _nsvect_free((v), (c), (s), (f)); \
+ (v) = NULL; \
+ } while (/*CONSTCOND*/0)
static int
! _nsdbtcmp(const void *a, const void *b)
{
+
return (strcasecmp(((const ns_dbt *)a)->name,
((const ns_dbt *)b)->name));
}
+ static int
+ _nsmodcmp(const void *a, const void *b)
+ {
+
+ return (strcasecmp(((const ns_mod *)a)->name,
+ ((const ns_mod *)b)->name));
+ }
+
+ static int
+ _nsmtabcmp(const void *a, const void *b)
+ {
+ int cmp;
+
+ cmp = strcmp(((const ns_mtab *)a)->name,
+ ((const ns_mtab *)b)->name);
+ if (cmp)
+ return (cmp);
+
+ return (strcasecmp(((const ns_mtab *)a)->database,
+ ((const ns_mtab *)b)->database));
+ }
+
+ static void
+ _nsmodfree(ns_mod *mod)
+ {
+
+ /*LINTED const cast*/
+ free((void *)mod->name);
+ if (mod->handle == NULL)
+ return;
+ if (mod->unregister != NULL)
+ (*mod->unregister)(mod->mtab, mod->mtabsize);
+ #ifdef __ELF__
+ if (mod->handle != _nsbuiltin)
+ (void) dlclose(mod->handle);
+ #endif /* __ELF__ */
+ }
+
+ /*
+ * Load a built-in or dyanamically linked module. If the `reg_fn'
+ * argument is non-NULL, assume a built-in module and use `reg_fn'
+ * to register it. Otherwise, search for a dynamic nsswitch module.
+ */
+ static int
+ _nsloadmod(const char *source, nss_module_register_fn reg_fn)
+ {
+ char buf[PATH_MAX];
+ ns_mod mod, *new;
+
+ memset(&mod, 0, sizeof(mod));
+ mod.name = strdup(source);
+ if (mod.name == NULL)
+ return (-1);
+
+ if (reg_fn != NULL) {
+ /*
+ * The placeholder is required, as a NULL handle
+ * represents an invalid module.
+ */
+ mod.handle = _nsbuiltin;
+ } else if (!is_dynamic()) {
+ goto out;
+ } else {
+ #ifdef __ELF__
+ if (snprintf(buf, sizeof(buf), "nss_%s.so.%d", mod.name,
+ NSS_MODULE_INTERFACE_VERSION) >= (int)sizeof(buf))
+ goto out;
+ mod.handle = dlopen(buf, RTLD_LOCAL | RTLD_LAZY);
+ if (mod.handle == NULL) {
+ #ifdef _NSS_DEBUG
+ /*
+ * This gets pretty annoying, since the built-in
+ * sources are not yet modules.
+ */
+ /* XXX log some error? */
+ #endif
+ goto out;
+ }
+ reg_fn = (nss_module_register_fn) dlsym(mod.handle,
+ "nss_module_register");
+ if (reg_fn == NULL) {
+ (void) dlclose(mod.handle);
+ mod.handle = NULL;
+ /* XXX log some error? */
+ goto out;
+ }
+ #else /* ! __ELF__ */
+ mod.handle = NULL;
+ #endif /* __ELF__ */
+ }
+ mod.mtab = (*reg_fn)(mod.name, &mod.mtabsize, &mod.unregister);
+ if (mod.mtab == NULL || mod.mtabsize == 0) {
+ #ifdef __ELF__
+ if (mod.handle != _nsbuiltin)
+ (void) dlclose(mod.handle);
+ #endif /* __ELF__ */
+ mod.handle = NULL;
+ /* XXX log some error? */
+ goto out;
+ }
+ if (mod.mtabsize > 1)
+ qsort(mod.mtab, mod.mtabsize, sizeof(mod.mtab[0]),
+ _nsmtabcmp);
+ out:
+ new = _nsvect_append(&mod, _nsmod, &_nsmodsize, sizeof(*_nsmod));
+ if (new == NULL) {
+ _nsmodfree(&mod);
+ return (-1);
+ }
+ _nsmod = new;
+ /* _nsmodsize already incremented */
+
+ qsort(_nsmod, _nsmodsize, sizeof(*_nsmod), _nsmodcmp);
+ return (0);
+ }
+
+ static void
+ _nsloadbuiltin(void)
+ {
+
+ /* Do nothing, for now. */
+ }
int
_nsdbtaddsrc(ns_dbt *dbt, const ns_src *src)
{
+ void *new;
+ const ns_mod *mod;
+ ns_mod modkey;
_DIAGASSERT(dbt != NULL);
_DIAGASSERT(src != NULL);
! new = _nsvect_append(src, dbt->srclist, &dbt->srclistsize,
! sizeof(*src));
! if (new == NULL)
! return (-1);
! dbt->srclist = new;
! /* dbt->srclistsize already incremented */
!
! modkey.name = src->name;
! mod = bsearch(&modkey, _nsmod, _nsmodsize, sizeof(*_nsmod),
! _nsmodcmp);
! if (mod == NULL)
! return (_nsloadmod(src->name, NULL));
return (0);
}
void
_nsdbtdump(const ns_dbt *dbt)
{
! int i;
_DIAGASSERT(dbt != NULL);
*************** _nsdbtdump(const ns_dbt *dbt)
*** 153,247 ****
printf("\n");
}
!
! const ns_dbt *
! _nsdbtget(const char *name)
{
! static time_t confmod;
!
! struct stat statbuf;
! ns_dbt dbt;
!
! _DIAGASSERT(name != NULL);
!
! dbt.name = name;
! NSLOCK();
! if (stat(_PATH_NS_CONF, &statbuf) == -1)
! return (NULL);
! if (confmod) {
! if (confmod < statbuf.st_mtime) {
! int i, j;
!
! for (i = 0; i < _nsmapsize; i++) {
! for (j = 0; j < _nsmap[i].srclistsize; j++) {
! if (_nsmap[i].srclist[j].name != NULL) {
! /*LINTED const cast*/
! free((void *)
! _nsmap[i].srclist[j].name);
! }
! }
! if (_nsmap[i].srclist)
! free(_nsmap[i].srclist);
! if (_nsmap[i].name) {
! /*LINTED const cast*/
! free((void *)_nsmap[i].name);
! }
! }
! if (_nsmap)
! free(_nsmap);
! _nsmap = NULL;
! _nsmapsize = 0;
! confmod = 0;
}
}
! if (!confmod) {
! _nsyyin = fopen(_PATH_NS_CONF, "r");
! if (_nsyyin == NULL) {
! NSUNLOCK();
! return (NULL);
! }
! _nsyyparse();
! (void)fclose(_nsyyin);
! qsort(_nsmap, (size_t)_nsmapsize, sizeof(ns_dbt), _nscmp);
! confmod = statbuf.st_mtime;
! }
! NSUNLOCK();
! return (bsearch(&dbt, _nsmap, (size_t)_nsmapsize, sizeof(ns_dbt),
! _nscmp));
}
int
_nsdbtput(const ns_dbt *dbt)
{
int i;
_DIAGASSERT(dbt != NULL);
for (i = 0; i < _nsmapsize; i++) {
! if (_nscmp(dbt, &_nsmap[i]) == 0) {
/* overwrite existing entry */
! if (_nsmap[i].srclist != NULL)
! free(_nsmap[i].srclist);
! memmove(&_nsmap[i], dbt, sizeof(ns_dbt));
return (0);
}
}
! if ((_nsmapsize % NSELEMSPERCHUNK) == 0) {
! ns_dbt *new;
! new = (ns_dbt *)realloc(_nsmap,
! (_nsmapsize + NSELEMSPERCHUNK) * sizeof(ns_dbt));
! if (new == NULL)
! return (-1);
! _nsmap = new;
}
! memmove(&_nsmap[_nsmapsize++], dbt, sizeof(ns_dbt));
return (0);
}
int
/*ARGSUSED*/
--- 388,534 ----
printf("\n");
}
! static void
! _nssrclist_free(ns_src **src, int srclistsize)
{
! int i;
! for (i = 0; i < srclistsize; i++) {
! if ((*src)[i].name != NULL) {
! /*LINTED const cast*/
! free((void *)(*src)[i].name);
}
}
! free(*src);
! *src = NULL;
}
+ static void
+ _nsdbtfree(ns_dbt *dbt)
+ {
+
+ _nssrclist_free(&dbt->srclist, dbt->srclistsize);
+ if (dbt->name != NULL) {
+ /*LINTED const cast*/
+ free((void *)dbt->name);
+ }
+ }
int
_nsdbtput(const ns_dbt *dbt)
{
+ ns_dbt *p;
+ void *new;
int i;
_DIAGASSERT(dbt != NULL);
for (i = 0; i < _nsmapsize; i++) {
! p = _nsvect_elem(i, _nsmap, _nsmapsize, sizeof(*_nsmap));
! if (strcasecmp(dbt->name, p->name) == 0) {
/* overwrite existing entry */
! if (p->srclist != NULL)
! _nssrclist_free(&p->srclist, p->srclistsize);
! memmove(p, dbt, sizeof(*dbt));
return (0);
}
}
+ new = _nsvect_append(dbt, _nsmap, &_nsmapsize, sizeof(*_nsmap));
+ if (new == NULL)
+ return (-1);
+ _nsmap = new;
+ /* _nsmapsize already incremented */
! return (0);
! }
! /*
! * This function is called each time nsdispatch() is called. If this
! * is the first call, or if the configuration has changed, (re-)prepare
! * the global data used by NSS.
! */
! static int
! _nsconfigure(void)
! {
! static time_t confmod;
! struct stat statbuf;
!
! rwlock_wrlock(&_nslock);
! if (stat(_PATH_NS_CONF, &statbuf) == -1) {
! /*
! * No nsswitch.conf; just use the defaults specified by
! * the calling routine.
! */
! rwlock_unlock(&_nslock);
! return (0);
! }
! if (statbuf.st_mtime <= confmod) {
! /* Internal state is up-to-date with nsswitch.conf. */
! rwlock_unlock(&_nslock);
! return (0);
! }
!
! _nsyyin = fopen(_PATH_NS_CONF, "r");
! if (_nsyyin == NULL) {
! /*
! * Unable to open nsswitch.conf; just use the existing
! * configuration.
! */
! rwlock_unlock(&_nslock);
! return (0);
}
!
! _NSVECT_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap),
! (_nsvect_free_elem) _nsdbtfree);
! _NSVECT_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod),
! (_nsvect_free_elem) _nsmodfree);
!
! _nsloadbuiltin();
!
! _nsyyparse();
! (void) fclose(_nsyyin);
! if (_nsmapsize != 0)
! qsort(_nsmap, _nsmapsize, sizeof(*_nsmap), _nsdbtcmp);
! confmod = statbuf.st_mtime;
! rwlock_unlock(&_nslock);
!
return (0);
}
+ static nss_method
+ _nsmethod(const char *source, const char *database, const char *method,
+ const ns_dtab disp_tab[], void **cb_data)
+ {
+ int curdisp;
+ ns_mod *mod, modkey;
+ ns_mtab *mtab, mtabkey;
+
+ if (disp_tab != NULL) {
+ for (curdisp = 0; disp_tab[curdisp].src != NULL; curdisp++) {
+ if (strcasecmp(source, disp_tab[curdisp].src) == 0) {
+ *cb_data = disp_tab[curdisp].cb_data;
+ return (disp_tab[curdisp].callback);
+ }
+ }
+ }
+
+ modkey.name = source;
+ mod = bsearch(&modkey, _nsmod, _nsmodsize, sizeof(*_nsmod),
+ _nsmodcmp);
+ if (mod != NULL && mod->handle != NULL) {
+ mtabkey.database = database;
+ mtabkey.name = method;
+ mtab = bsearch(&mtabkey, mod->mtab, mod->mtabsize,
+ sizeof(mod->mtab[0]), _nsmtabcmp);
+ if (mtab != NULL) {
+ *cb_data = mtab->mdata;
+ return (mtab->method);
+ }
+ }
+
+ *cb_data = NULL;
+ return (NULL);
+ }
int
/*ARGSUSED*/
*************** nsdispatch(void *retval, const ns_dtab d
*** 249,265 ****
const char *method, const ns_src defaults[], ...)
{
va_list ap;
! int i, curdisp, result;
const ns_dbt *dbt;
const ns_src *srclist;
int srclistsize;
_DIAGASSERT(database != NULL);
_DIAGASSERT(method != NULL);
if (database == NULL || method == NULL)
return (NS_UNAVAIL);
! dbt = _nsdbtget(database);
if (dbt != NULL) {
srclist = dbt->srclist;
srclistsize = dbt->srclistsize;
--- 536,561 ----
const char *method, const ns_src defaults[], ...)
{
va_list ap;
! int i, result;
! ns_dbt key;
const ns_dbt *dbt;
const ns_src *srclist;
int srclistsize;
+ nss_method cb;
+ void *cb_data;
_DIAGASSERT(database != NULL);
_DIAGASSERT(method != NULL);
if (database == NULL || method == NULL)
return (NS_UNAVAIL);
! if (_nsconfigure())
! return (NS_UNAVAIL);
!
! rwlock_rdlock(&_nslock);
!
! key.name = database;
! dbt = bsearch(&key, _nsmap, _nsmapsize, sizeof(*_nsmap), _nsdbtcmp);
if (dbt != NULL) {
srclist = dbt->srclist;
srclistsize = dbt->srclistsize;
*************** nsdispatch(void *retval, const ns_dtab d
*** 272,291 ****
result = 0;
for (i = 0; i < srclistsize; i++) {
! for (curdisp = 0; disp_tab[curdisp].src != NULL; curdisp++)
! if (strcasecmp(disp_tab[curdisp].src,
! srclist[i].name) == 0)
! break;
result = 0;
! if (disp_tab[curdisp].callback) {
va_start(ap, defaults);
! result = disp_tab[curdisp].callback(retval,
! disp_tab[curdisp].cb_data, ap);
va_end(ap);
! if (result & srclist[i].flags) {
break;
- }
}
}
return (result ? result : NS_NOTFOUND);
}
--- 568,586 ----
result = 0;
for (i = 0; i < srclistsize; i++) {
! cb = _nsmethod(srclist[i].name, database, method,
! disp_tab, &cb_data);
result = 0;
! if (cb != NULL) {
va_start(ap, defaults);
! result = (*cb)(retval, cb_data, ap);
va_end(ap);
! if (result & srclist[i].flags)
break;
}
}
+
+ rwlock_unlock(&_nslock);
+
return (result ? result : NS_NOTFOUND);
}
--Apple-Mail-22-217596071--
--Apple-Mail-23-217596080
content-type: application/pgp-signature; x-mac-type=70674453;
name=PGP.sig
content-description: This is a digitally signed message part
content-disposition: inline; filename=PGP.sig
content-transfer-encoding: 7bit
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.3 (Darwin)
iD8DBQFA+woZOpVKkaBm8XkRAqmKAJ9mhhIpJPd3k2TYI9EUJX+zT93mlQCg0Fi4
Vz1NKk3Rmrpj6AXq4QSiO0Y=
=VCHh
-----END PGP SIGNATURE-----
--Apple-Mail-23-217596080--