Add support for dynamically loading nsswitch modules on ELF platforms.

Adapted from FreeBSD.  Maintains full backward API / ABI compatbility
with built-in-only nsdispatch().

While here, also make nsdispatch() itself thread-safe.
This commit is contained in:
thorpej 2004-07-24 18:42:51 +00:00
parent 940bb13e09
commit 43d6d8d887
3 changed files with 612 additions and 144 deletions

@ -1,4 +1,4 @@
/* $NetBSD: nsswitch.h,v 1.12 2003/07/09 01:59:34 kristerw Exp $ */
/* $NetBSD: nsswitch.h,v 1.13 2004/07/24 18:42:51 thorpej Exp $ */
/*-
* Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc.
@ -49,6 +49,8 @@
#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
@ -102,13 +104,18 @@
#define NSDB_TERMCAP "termcap"
#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;
int (*callback) __P((void *, void *, _BSD_VA_LIST_));
nss_method callback;
void *cb_data;
} ns_dtab;
@ -148,6 +155,29 @@ typedef struct {
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
/*
@ -162,9 +192,21 @@ extern const ns_src __nsdefaultsrc[];
typedef struct {
const char *name; /* name of database */
ns_src *srclist; /* list of sources */
int srclistsize; /* size of srclist */
u_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 */
@ -177,7 +219,6 @@ int nsdispatch __P((void *, const ns_dtab [], const char *,
#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));

@ -1,10 +1,10 @@
.\" $NetBSD: nsdispatch.3,v 1.15 2003/07/26 19:24:48 salo Exp $
.\" $NetBSD: nsdispatch.3,v 1.16 2004/07/24 18:42:51 thorpej Exp $
.\"
.\" Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc.
.\" 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.
.\" 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,7 +34,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd January 19, 1999
.Dd July 18, 2004
.Dt NSDISPATCH 3
.Os
.Sh NAME
@ -67,7 +67,12 @@ until a successful entry is found.
.Va retval
is passed to each callback function to modify as necessary
(to pass back to the caller of
.Fn nsdispatch )
.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
@ -76,19 +81,22 @@ structures, which have the following format:
.Bd -literal -offset indent
typedef struct {
const char *src;
int (*cb)(void *retval, void *cb_data, va_list ap);
nss_method cb;
void *cb_data;
} ns_dtab;
.Ed
.Pp
.Bd -ragged -offset indent
For each source type that is implemented, an entry with
The
.Fa dtab
array should consist of one entry for each source type that is implemented,
with
.Va src
set to the name of the source,
as the name of the source,
.Va cb
defined as a function which handles that source, and
as a function which handles that source, and
.Va cb_data
is used to pass arbitrary data to the callback function.
as a pointer to arbitrary data to be passed to the callback.
The last entry in
.Va dtab
should contain
@ -100,14 +108,19 @@ and
.Va cb_data .
.Ed
.Pp
.Va method
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 .
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.
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
@ -126,18 +139,21 @@ typedef struct {
.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
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
.Va defaults
.Fa defaults
should have
.Va src
set to
@ -158,10 +174,88 @@ are optional extra arguments, which
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
Whilst there is support for arbitrary sources, the following
#defines for commonly implemented sources are available:
.Bl -column NS_COMPAT COMPAT -offset indent
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"
@ -172,6 +266,22 @@ Whilst there is support for arbitrary sources, the following
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:
@ -192,6 +302,7 @@ returns the value of the callback that caused the dispatcher to finish,
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
@ -200,6 +311,8 @@ The
.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
@ -211,14 +324,8 @@ and
.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 .
Support for dynamically-loaded modules was added by Jason Thorpe
.Aq thorpej@NetBSD.org ,
based on code developed by the
.Fx
Project.

@ -1,11 +1,11 @@
/* $NetBSD: nsdispatch.c,v 1.21 2004/07/16 16:11:43 thorpej Exp $ */
/* $NetBSD: nsdispatch.c,v 1.22 2004/07/24 18:42:51 thorpej Exp $ */
/*-
* Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc.
* 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.
* 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,9 +36,41 @@
* 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.22 2004/07/24 18:42:51 thorpej Exp $");
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
@ -48,6 +80,9 @@ __RCSID("$NetBSD: nsdispatch.c,v 1.21 2004/07/16 16:11:43 thorpej Exp $");
#include <sys/stat.h>
#include <assert.h>
#ifdef __ELF__
#include <dlfcn.h>
#endif /* __ELF__ */
#include <err.h>
#include <fcntl.h>
#define _NS_PRIVATE
@ -57,7 +92,8 @@ __RCSID("$NetBSD: nsdispatch.c,v 1.21 2004/07/16 16:11:43 thorpej Exp $");
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <threadlib.h>
#include "reentrant.h"
extern FILE *_nsyyin;
extern int _nsyyparse(void);
@ -76,58 +112,258 @@ const ns_src __nsdefaultsrc[] = {
{ 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;
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()
/*
* 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
/*
* size of dynamic array chunk for _nsmap and _nsmap[x].srclist
* 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
_nscmp(const void *a, const void *b)
_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);
if ((dbt->srclistsize % NSELEMSPERCHUNK) == 0) {
ns_src *new;
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));
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;
int i;
_DIAGASSERT(dbt != NULL);
@ -153,95 +389,171 @@ _nsdbtdump(const ns_dbt *dbt)
printf("\n");
}
const ns_dbt *
_nsdbtget(const char *name)
static void
_nssrclist_free(ns_src **src, u_int srclistsize)
{
static time_t confmod;
u_int i;
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;
for (i = 0; i < srclistsize; i++) {
if ((*src)[i].name != NULL) {
/*LINTED const cast*/
free((void *)(*src)[i].name);
}
}
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));
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)
{
int i;
ns_dbt *p;
void *new;
u_int i;
_DIAGASSERT(dbt != NULL);
for (i = 0; i < _nsmapsize; i++) {
if (_nscmp(dbt, &_nsmap[i]) == 0) {
p = _nsvect_elem(i, _nsmap, _nsmapsize, sizeof(*_nsmap));
if (strcasecmp(dbt->name, p->name) == 0) {
/* overwrite existing entry */
if (_nsmap[i].srclist != NULL)
free(_nsmap[i].srclist);
memmove(&_nsmap[i], dbt, sizeof(ns_dbt));
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 */
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);
}
/*
* 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)
{
#ifdef _REENTRANT
static mutex_t _nsconflock = MUTEX_INITIALIZER;
#endif
static time_t _nsconfmod;
struct stat statbuf;
mutex_lock(&_nsconflock);
if (stat(_PATH_NS_CONF, &statbuf) == -1) {
/*
* No nsswitch.conf; just use whatever configuration we
* currently have, or fall back on the defaults specified
* by the caller.
*/
mutex_unlock(&_nsconflock);
return (0);
}
if (statbuf.st_mtime <= _nsconfmod) {
/* Internal state is up-to-date with nsswitch.conf. */
mutex_unlock(&_nsconflock);
return (0);
}
/*
* Ok, we've decided we need to update the nsswitch configuration
* structures. Update the timestamp, acquire a write-lock on
* _nslock, and then release _nsconflock. This means that we don't
* need to acquire _nsconflock again to update the timetamp, and
* prevents another thread from updating the configuration before
* we're finished, even if they decide that they need to.
*
* Acquiring the locks in this fashion is safe: Only here are
* both _nslock and _nsconflock both taken, and nsdispatch()
* should never be called recursively.
*/
_nsconfmod = statbuf.st_mtime;
rwlock_wrlock(&_nslock);
mutex_unlock(&_nsconflock);
_nsyyin = fopen(_PATH_NS_CONF, "r");
if (_nsyyin == NULL) {
/*
* Unable to open nsswitch.conf; behave as though the
* stat() above failed. Even though we have already
* updated _nsconfmod, if the file reappears, the
* mtime will change.
*/
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);
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*/
@ -249,17 +561,26 @@ nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database,
const char *method, const ns_src defaults[], ...)
{
va_list ap;
int i, curdisp, result;
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);
dbt = _nsdbtget(database);
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;
@ -272,20 +593,19 @@ nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database,
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;
cb = _nsmethod(srclist[i].name, database, method,
disp_tab, &cb_data);
result = 0;
if (disp_tab[curdisp].callback) {
if (cb != NULL) {
va_start(ap, defaults);
result = disp_tab[curdisp].callback(retval,
disp_tab[curdisp].cb_data, ap);
result = (*cb)(retval, cb_data, ap);
va_end(ap);
if (result & srclist[i].flags) {
if (result & srclist[i].flags)
break;
}
}
}
rwlock_unlock(&_nslock);
return (result ? result : NS_NOTFOUND);
}