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--