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:
parent
940bb13e09
commit
43d6d8d887
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user