1 /* $NetBSD: nsdispatch.c,v 1.33 2008/08/17 10:51:19 gmcgarry Exp $ */
4 * Copyright (c) 1997, 1998, 1999, 2004 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn; and by Jason R. Thorpe.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Copyright (c) 2003 Networks Associates Technology, Inc.
34 * All rights reserved.
36 * Portions of this software were developed for the FreeBSD Project by
37 * Jacques A. Vidrine, Safeport Network Services, and Network
38 * Associates Laboratories, the Security Research Division of Network
39 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
40 * ("CBOSS"), as part of the DARPA CHATS research program.
42 * Redistribution and use in source and binary forms, with or without
43 * modification, are permitted provided that the following conditions
45 * 1. Redistributions of source code must retain the above copyright
46 * notice, this list of conditions and the following disclaimer.
47 * 2. Redistributions in binary form must reproduce the above copyright
48 * notice, this list of conditions and the following disclaimer in the
49 * documentation and/or other materials provided with the distribution.
51 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 #include <sys/cdefs.h>
65 #if defined(LIBC_SCCS) && !defined(lint)
66 __RCSID("$NetBSD: nsdispatch.c,v 1.33 2008/08/17 10:51:19 gmcgarry Exp $");
67 #endif /* LIBC_SCCS and not lint */
69 #include "namespace.h"
71 #include <sys/types.h>
72 #include <sys/param.h>
74 #include <sys/queue.h>
90 #include "reentrant.h"
93 extern int _nsyyparse(void);
97 __weak_alias(nsdispatch
,_nsdispatch
)
102 * default sourcelist: `files'
104 const ns_src __nsdefaultsrc
[] = {
105 { NSSRC_FILES
, NS_SUCCESS
},
109 const ns_src __nsdefaultcompat
[] = {
110 { NSSRC_COMPAT
, NS_SUCCESS
},
114 const ns_src __nsdefaultcompat_forceall
[] = {
115 { NSSRC_COMPAT
, NS_SUCCESS
| NS_FORCEALL
},
119 const ns_src __nsdefaultfiles
[] = {
120 { NSSRC_FILES
, NS_SUCCESS
},
124 const ns_src __nsdefaultfiles_forceall
[] = {
125 { NSSRC_FILES
, NS_SUCCESS
| NS_FORCEALL
},
129 const ns_src __nsdefaultnis
[] = {
130 { NSSRC_NIS
, NS_SUCCESS
},
134 const ns_src __nsdefaultnis_forceall
[] = {
135 { NSSRC_NIS
, NS_SUCCESS
| NS_FORCEALL
},
140 /* Database, source mappings. */
141 static u_int _nsmapsize
;
142 static ns_dbt
*_nsmap
;
144 /* Nsswitch modules. */
145 static u_int _nsmodsize
;
146 static ns_mod
*_nsmod
;
148 /* Placeholder for built-in modules' dlopen() handles. */
149 static void *_nsbuiltin
= &_nsbuiltin
;
153 * Global nsswitch data structures are mostly read-only, but we update them
154 * when we read or re-read nsswitch.conf.
156 static rwlock_t _nslock
= RWLOCK_INITIALIZER
;
159 * List of threads currently in nsdispatch(). We use this to detect
160 * recursive calls and avoid reloading configuration in such cases,
161 * which could cause deadlock.
164 LIST_ENTRY(_ns_drec
) list
;
167 static LIST_HEAD(, _ns_drec
) _ns_drec
= LIST_HEAD_INITIALIZER(&_ns_drec
);
168 static mutex_t _ns_drec_lock
= MUTEX_INITIALIZER
;
169 #endif /* _REENTRANT */
173 * Runtime determination of whether we are dynamically linked or not.
176 extern int _DYNAMIC
__weak_reference(_DYNAMIC
);
177 #define is_dynamic() (&_DYNAMIC != NULL)
179 #define is_dynamic() (0) /* don't bother - switch to ELF! */
184 * size of dynamic array chunk for _nsmap and _nsmap[x].srclist (and other
187 #define NSELEMSPERCHUNK 8
190 * Dynamically growable arrays are used for lists of databases, sources,
191 * and modules. The following "vector" API is used to isolate the
194 typedef void (*_nsvect_free_elem
)(void *);
197 _nsvect_append(const void *elem
, void *vec
, u_int
*count
, size_t esize
)
201 if ((*count
% NSELEMSPERCHUNK
) == 0) {
202 p
= realloc(vec
, (*count
+ NSELEMSPERCHUNK
) * esize
);
207 memmove((void *)(((uintptr_t)vec
) + (*count
* esize
)), elem
, esize
);
213 _nsvect_elem(u_int i
, void *vec
, u_int count
, size_t esize
)
217 return ((void *)((uintptr_t)vec
+ (i
* esize
)));
223 _nsvect_free(void *vec
, u_int
*count
, size_t esize
, _nsvect_free_elem free_elem
)
228 for (i
= 0; i
< *count
; i
++) {
229 elem
= _nsvect_elem(i
, vec
, *count
, esize
);
237 #define _NSVECT_FREE(v, c, s, f) \
239 _nsvect_free((v), (c), (s), (f)); \
241 } while (/*CONSTCOND*/0)
244 _nsdbtcmp(const void *a
, const void *b
)
247 return (strcasecmp(((const ns_dbt
*)a
)->name
,
248 ((const ns_dbt
*)b
)->name
));
252 _nsmodcmp(const void *a
, const void *b
)
255 return (strcasecmp(((const ns_mod
*)a
)->name
,
256 ((const ns_mod
*)b
)->name
));
260 _nsmtabcmp(const void *a
, const void *b
)
264 cmp
= strcmp(((const ns_mtab
*)a
)->name
,
265 ((const ns_mtab
*)b
)->name
);
269 return (strcasecmp(((const ns_mtab
*)a
)->database
,
270 ((const ns_mtab
*)b
)->database
));
274 _nsmodfree(ns_mod
*mod
)
277 free(__UNCONST(mod
->name
));
278 if (mod
->handle
== NULL
)
280 if (mod
->unregister
!= NULL
)
281 (*mod
->unregister
)(mod
->mtab
, mod
->mtabsize
);
283 if (mod
->handle
!= _nsbuiltin
)
284 (void) dlclose(mod
->handle
);
289 * Load a built-in or dyanamically linked module. If the `reg_fn'
290 * argument is non-NULL, assume a built-in module and use `reg_fn'
291 * to register it. Otherwise, search for a dynamic nsswitch module.
294 _nsloadmod(const char *source
, nss_module_register_fn reg_fn
)
301 memset(&mod
, 0, sizeof(mod
));
302 mod
.name
= strdup(source
);
303 if (mod
.name
== NULL
)
306 if (reg_fn
!= NULL
) {
308 * The placeholder is required, as a NULL handle
309 * represents an invalid module.
311 mod
.handle
= _nsbuiltin
;
312 } else if (!is_dynamic()) {
316 if (snprintf(buf
, sizeof(buf
), "nss_%s.so.%d", mod
.name
,
317 NSS_MODULE_INTERFACE_VERSION
) >= (int)sizeof(buf
))
319 mod
.handle
= dlopen(buf
, RTLD_LOCAL
| RTLD_LAZY
);
320 if (mod
.handle
== NULL
) {
323 * This gets pretty annoying, since the built-in
324 * sources are not yet modules.
326 /* XXX log some error? */
330 reg_fn
= (nss_module_register_fn
) dlsym(mod
.handle
,
331 "nss_module_register");
332 if (reg_fn
== NULL
) {
333 (void) dlclose(mod
.handle
);
335 /* XXX log some error? */
338 #else /* ! __ELF__ */
342 mod
.mtab
= (*reg_fn
)(mod
.name
, &mod
.mtabsize
, &mod
.unregister
);
343 if (mod
.mtab
== NULL
|| mod
.mtabsize
== 0) {
345 if (mod
.handle
!= _nsbuiltin
)
346 (void) dlclose(mod
.handle
);
349 /* XXX log some error? */
352 if (mod
.mtabsize
> 1)
353 qsort(mod
.mtab
, mod
.mtabsize
, sizeof(mod
.mtab
[0]),
356 new = _nsvect_append(&mod
, _nsmod
, &_nsmodsize
, sizeof(*_nsmod
));
362 /* _nsmodsize already incremented */
364 qsort(_nsmod
, _nsmodsize
, sizeof(*_nsmod
), _nsmodcmp
);
372 /* Do nothing, for now. */
376 _nsdbtaddsrc(ns_dbt
*dbt
, const ns_src
*src
)
382 _DIAGASSERT(dbt
!= NULL
);
383 _DIAGASSERT(src
!= NULL
);
385 new = _nsvect_append(src
, dbt
->srclist
, &dbt
->srclistsize
,
390 /* dbt->srclistsize already incremented */
392 modkey
.name
= src
->name
;
393 mod
= bsearch(&modkey
, _nsmod
, _nsmodsize
, sizeof(*_nsmod
),
396 return (_nsloadmod(src
->name
, NULL
));
402 _nsdbtdump(const ns_dbt
*dbt
)
406 _DIAGASSERT(dbt
!= NULL
);
408 printf("%s (%d source%s):", dbt
->name
, dbt
->srclistsize
,
409 dbt
->srclistsize
== 1 ? "" : "s");
410 for (i
= 0; i
< dbt
->srclistsize
; i
++) {
411 printf(" %s", dbt
->srclist
[i
].name
);
412 if (!(dbt
->srclist
[i
].flags
&
413 (NS_UNAVAIL
|NS_NOTFOUND
|NS_TRYAGAIN
)) &&
414 (dbt
->srclist
[i
].flags
& NS_SUCCESS
))
417 if (!(dbt
->srclist
[i
].flags
& NS_SUCCESS
))
418 printf(" SUCCESS=continue");
419 if (dbt
->srclist
[i
].flags
& NS_UNAVAIL
)
420 printf(" UNAVAIL=return");
421 if (dbt
->srclist
[i
].flags
& NS_NOTFOUND
)
422 printf(" NOTFOUND=return");
423 if (dbt
->srclist
[i
].flags
& NS_TRYAGAIN
)
424 printf(" TRYAGAIN=return");
431 _nssrclist_free(ns_src
**src
, u_int srclistsize
)
435 for (i
= 0; i
< srclistsize
; i
++) {
436 if ((*src
)[i
].name
!= NULL
)
437 free(__UNCONST((*src
)[i
].name
));
444 _nsdbtfree(ns_dbt
*dbt
)
447 _nssrclist_free(&dbt
->srclist
, dbt
->srclistsize
);
448 if (dbt
->name
!= NULL
)
449 free(__UNCONST(dbt
->name
));
453 _nsdbtput(const ns_dbt
*dbt
)
459 _DIAGASSERT(dbt
!= NULL
);
461 for (i
= 0; i
< _nsmapsize
; i
++) {
462 p
= _nsvect_elem(i
, _nsmap
, _nsmapsize
, sizeof(*_nsmap
));
463 if (strcasecmp(dbt
->name
, p
->name
) == 0) {
464 /* overwrite existing entry */
465 if (p
->srclist
!= NULL
)
466 _nssrclist_free(&p
->srclist
, p
->srclistsize
);
467 memmove(p
, dbt
, sizeof(*dbt
));
471 new = _nsvect_append(dbt
, _nsmap
, &_nsmapsize
, sizeof(*_nsmap
));
475 /* _nsmapsize already incremented */
481 * This function is called each time nsdispatch() is called. If this
482 * is the first call, or if the configuration has changed, (re-)prepare
483 * the global data used by NSS.
489 static mutex_t _nsconflock
= MUTEX_INITIALIZER
;
491 static time_t _nsconfmod
;
494 mutex_lock(&_nsconflock
);
496 if (stat(_PATH_NS_CONF
, &statbuf
) == -1) {
498 * No nsswitch.conf; just use whatever configuration we
499 * currently have, or fall back on the defaults specified
502 mutex_unlock(&_nsconflock
);
506 if (statbuf
.st_mtime
<= _nsconfmod
) {
507 /* Internal state is up-to-date with nsswitch.conf. */
508 mutex_unlock(&_nsconflock
);
513 * Ok, we've decided we need to update the nsswitch configuration
514 * structures. Acquire a write-lock on _nslock while continuing
515 * to hold _nsconflock. Acquiring a write-lock blocks while
516 * waiting for other threads already holding a read-lock to clear.
517 * We hold _nsconflock for the duration, and update the time stamp
518 * at the end of the update operation, at which time we release
521 rwlock_wrlock(&_nslock
);
523 _nsyyin
= fopen(_PATH_NS_CONF
, "r");
524 if (_nsyyin
== NULL
) {
526 * Unable to open nsswitch.conf; behave as though the
527 * stat() above failed. Even though we have already
528 * updated _nsconfmod, if the file reappears, the
534 _NSVECT_FREE(_nsmap
, &_nsmapsize
, sizeof(*_nsmap
),
535 (_nsvect_free_elem
) _nsdbtfree
);
536 _NSVECT_FREE(_nsmod
, &_nsmodsize
, sizeof(*_nsmod
),
537 (_nsvect_free_elem
) _nsmodfree
);
542 (void) fclose(_nsyyin
);
544 qsort(_nsmap
, _nsmapsize
, sizeof(*_nsmap
), _nsdbtcmp
);
546 _nsconfmod
= statbuf
.st_mtime
;
549 rwlock_unlock(&_nslock
);
550 mutex_unlock(&_nsconflock
);
555 _nsmethod(const char *source
, const char *database
, const char *method
,
556 const ns_dtab disp_tab
[], void **cb_data
)
560 ns_mtab
*mtab
, mtabkey
;
562 if (disp_tab
!= NULL
) {
563 for (curdisp
= 0; disp_tab
[curdisp
].src
!= NULL
; curdisp
++) {
564 if (strcasecmp(source
, disp_tab
[curdisp
].src
) == 0) {
565 *cb_data
= disp_tab
[curdisp
].cb_data
;
566 return (disp_tab
[curdisp
].callback
);
571 modkey
.name
= source
;
572 mod
= bsearch(&modkey
, _nsmod
, _nsmodsize
, sizeof(*_nsmod
),
574 if (mod
!= NULL
&& mod
->handle
!= NULL
) {
575 mtabkey
.database
= database
;
576 mtabkey
.name
= method
;
577 mtab
= bsearch(&mtabkey
, mod
->mtab
, mod
->mtabsize
,
578 sizeof(mod
->mtab
[0]), _nsmtabcmp
);
580 *cb_data
= mtab
->mdata
;
581 return (mtab
->method
);
591 nsdispatch(void *retval
, const ns_dtab disp_tab
[], const char *database
,
592 const char *method
, const ns_src defaults
[], ...)
594 static int _nsdispatching
;
596 struct _ns_drec drec
, *ldrec
;
602 const ns_src
*srclist
;
607 /* retval may be NULL */
608 /* disp_tab may be NULL */
609 _DIAGASSERT(database
!= NULL
);
610 _DIAGASSERT(method
!= NULL
);
611 _DIAGASSERT(defaults
!= NULL
);
612 if (database
== NULL
|| method
== NULL
|| defaults
== NULL
)
616 * In both the threaded and non-threaded cases, avoid reloading
617 * the configuration if the current thread is already running
618 * nsdispatch() (i.e. recursive call).
620 * In the non-threaded case, this avoids changing the data structures
621 * while we're using them.
623 * In the threaded case, this avoids trying to take a write lock
624 * while the current thread holds a read lock (which would result
629 drec
.thr
= thr_self();
630 mutex_lock(&_ns_drec_lock
);
631 LIST_FOREACH(ldrec
, &_ns_drec
, list
) {
632 if (ldrec
->thr
== drec
.thr
)
635 LIST_INSERT_HEAD(&_ns_drec
, &drec
, list
);
636 mutex_unlock(&_ns_drec_lock
);
637 if (ldrec
== NULL
&& _nsconfigure()) {
638 mutex_lock(&_ns_drec_lock
);
639 LIST_REMOVE(&drec
, list
);
640 mutex_unlock(&_ns_drec_lock
);
644 #endif /* _REENTRANT */
645 if (_nsdispatching
++ == 0 && _nsconfigure()) {
650 rwlock_rdlock(&_nslock
);
653 dbt
= bsearch(&key
, _nsmap
, _nsmapsize
, sizeof(*_nsmap
), _nsdbtcmp
);
655 srclist
= dbt
->srclist
;
656 srclistsize
= dbt
->srclistsize
;
660 while (srclist
[srclistsize
].name
!= NULL
)
665 for (i
= 0; i
< srclistsize
; i
++) {
666 cb
= _nsmethod(srclist
[i
].name
, database
, method
,
670 va_start(ap
, defaults
);
671 result
= (*cb
)(retval
, cb_data
, ap
);
673 if (defaults
[0].flags
& NS_FORCEALL
)
675 if (result
& srclist
[i
].flags
)
679 result
&= NS_STATUSMASK
; /* clear private flags in result */
681 rwlock_unlock(&_nslock
);
685 mutex_lock(&_ns_drec_lock
);
686 LIST_REMOVE(&drec
, list
);
687 mutex_unlock(&_ns_drec_lock
);
689 #endif /* _REENTRANT */
692 return (result
? result
: NS_NOTFOUND
);