1 /* $NetBSD: nsdispatch.c,v 1.37 2012/03/13 21:13:42 christos 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.37 2012/03/13 21:13:42 christos 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 #define is_dynamic() (0) /* don't bother - switch to ELF! */
178 __weakref_visible
int rtld_DYNAMIC
__weak_reference(_DYNAMIC
);
179 #define is_dynamic() (&rtld_DYNAMIC != NULL)
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
), _nsmodcmp
);
395 return (_nsloadmod(src
->name
, NULL
));
401 _nsdbtdump(const ns_dbt
*dbt
)
405 _DIAGASSERT(dbt
!= NULL
);
407 printf("%s (%d source%s):", dbt
->name
, dbt
->srclistsize
,
408 dbt
->srclistsize
== 1 ? "" : "s");
409 for (i
= 0; i
< dbt
->srclistsize
; i
++) {
410 printf(" %s", dbt
->srclist
[i
].name
);
411 if (!(dbt
->srclist
[i
].flags
&
412 (NS_UNAVAIL
|NS_NOTFOUND
|NS_TRYAGAIN
)) &&
413 (dbt
->srclist
[i
].flags
& NS_SUCCESS
))
416 if (!(dbt
->srclist
[i
].flags
& NS_SUCCESS
))
417 printf(" SUCCESS=continue");
418 if (dbt
->srclist
[i
].flags
& NS_UNAVAIL
)
419 printf(" UNAVAIL=return");
420 if (dbt
->srclist
[i
].flags
& NS_NOTFOUND
)
421 printf(" NOTFOUND=return");
422 if (dbt
->srclist
[i
].flags
& NS_TRYAGAIN
)
423 printf(" TRYAGAIN=return");
430 _nssrclist_free(ns_src
**src
, u_int srclistsize
)
434 for (i
= 0; i
< srclistsize
; i
++) {
435 if ((*src
)[i
].name
!= NULL
)
436 free(__UNCONST((*src
)[i
].name
));
443 _nsdbtfree(ns_dbt
*dbt
)
446 _nssrclist_free(&dbt
->srclist
, dbt
->srclistsize
);
447 if (dbt
->name
!= NULL
)
448 free(__UNCONST(dbt
->name
));
452 _nsdbtput(const ns_dbt
*dbt
)
458 _DIAGASSERT(dbt
!= NULL
);
460 for (i
= 0; i
< _nsmapsize
; i
++) {
461 p
= _nsvect_elem(i
, _nsmap
, _nsmapsize
, sizeof(*_nsmap
));
462 if (strcasecmp(dbt
->name
, p
->name
) == 0) {
463 /* overwrite existing entry */
464 if (p
->srclist
!= NULL
)
465 _nssrclist_free(&p
->srclist
, p
->srclistsize
);
466 memmove(p
, dbt
, sizeof(*dbt
));
470 new = _nsvect_append(dbt
, _nsmap
, &_nsmapsize
, sizeof(*_nsmap
));
474 /* _nsmapsize already incremented */
480 * This function is called each time nsdispatch() is called. If this
481 * is the first call, or if the configuration has changed, (re-)prepare
482 * the global data used by NSS.
488 static mutex_t _nsconflock
= MUTEX_INITIALIZER
;
490 static time_t _nsconfmod
;
493 mutex_lock(&_nsconflock
);
495 if (stat(_PATH_NS_CONF
, &statbuf
) == -1) {
497 * No nsswitch.conf; just use whatever configuration we
498 * currently have, or fall back on the defaults specified
501 mutex_unlock(&_nsconflock
);
505 if (statbuf
.st_mtime
<= _nsconfmod
) {
506 /* Internal state is up-to-date with nsswitch.conf. */
507 mutex_unlock(&_nsconflock
);
512 * Ok, we've decided we need to update the nsswitch configuration
513 * structures. Acquire a write-lock on _nslock while continuing
514 * to hold _nsconflock. Acquiring a write-lock blocks while
515 * waiting for other threads already holding a read-lock to clear.
516 * We hold _nsconflock for the duration, and update the time stamp
517 * at the end of the update operation, at which time we release
520 rwlock_wrlock(&_nslock
);
522 _nsyyin
= fopen(_PATH_NS_CONF
, "r");
523 if (_nsyyin
== NULL
) {
525 * Unable to open nsswitch.conf; behave as though the
526 * stat() above failed. Even though we have already
527 * updated _nsconfmod, if the file reappears, the
533 _NSVECT_FREE(_nsmap
, &_nsmapsize
, sizeof(*_nsmap
),
534 (_nsvect_free_elem
) _nsdbtfree
);
535 _NSVECT_FREE(_nsmod
, &_nsmodsize
, sizeof(*_nsmod
),
536 (_nsvect_free_elem
) _nsmodfree
);
541 (void) fclose(_nsyyin
);
543 qsort(_nsmap
, _nsmapsize
, sizeof(*_nsmap
), _nsdbtcmp
);
545 _nsconfmod
= statbuf
.st_mtime
;
548 rwlock_unlock(&_nslock
);
549 mutex_unlock(&_nsconflock
);
554 _nsmethod(const char *source
, const char *database
, const char *method
,
555 const ns_dtab disp_tab
[], void **cb_data
)
559 ns_mtab
*mtab
, mtabkey
;
561 if (disp_tab
!= NULL
) {
562 for (curdisp
= 0; disp_tab
[curdisp
].src
!= NULL
; curdisp
++) {
563 if (strcasecmp(source
, disp_tab
[curdisp
].src
) == 0) {
564 *cb_data
= disp_tab
[curdisp
].cb_data
;
565 return (disp_tab
[curdisp
].callback
);
570 modkey
.name
= source
;
571 mod
= bsearch(&modkey
, _nsmod
, _nsmodsize
, sizeof(*_nsmod
),
573 if (mod
!= NULL
&& mod
->handle
!= NULL
) {
574 mtabkey
.database
= database
;
575 mtabkey
.name
= method
;
576 mtab
= bsearch(&mtabkey
, mod
->mtab
, mod
->mtabsize
,
577 sizeof(mod
->mtab
[0]), _nsmtabcmp
);
579 *cb_data
= mtab
->mdata
;
580 return (mtab
->method
);
590 nsdispatch(void *retval
, const ns_dtab disp_tab
[], const char *database
,
591 const char *method
, const ns_src defaults
[], ...)
593 static int _nsdispatching
;
595 struct _ns_drec drec
, *ldrec
;
601 const ns_src
*srclist
;
606 /* retval may be NULL */
607 /* disp_tab may be NULL */
608 _DIAGASSERT(database
!= NULL
);
609 _DIAGASSERT(method
!= NULL
);
610 _DIAGASSERT(defaults
!= NULL
);
611 if (database
== NULL
|| method
== NULL
|| defaults
== NULL
)
615 * In both the threaded and non-threaded cases, avoid reloading
616 * the configuration if the current thread is already running
617 * nsdispatch() (i.e. recursive call).
619 * In the non-threaded case, this avoids changing the data structures
620 * while we're using them.
622 * In the threaded case, this avoids trying to take a write lock
623 * while the current thread holds a read lock (which would result
628 drec
.thr
= thr_self();
629 mutex_lock(&_ns_drec_lock
);
630 LIST_FOREACH(ldrec
, &_ns_drec
, list
) {
631 if (ldrec
->thr
== drec
.thr
)
634 LIST_INSERT_HEAD(&_ns_drec
, &drec
, list
);
635 mutex_unlock(&_ns_drec_lock
);
636 if (ldrec
== NULL
&& _nsconfigure()) {
637 mutex_lock(&_ns_drec_lock
);
638 LIST_REMOVE(&drec
, list
);
639 mutex_unlock(&_ns_drec_lock
);
643 #endif /* _REENTRANT */
644 if (_nsdispatching
++ == 0 && _nsconfigure()) {
649 rwlock_rdlock(&_nslock
);
652 dbt
= bsearch(&key
, _nsmap
, _nsmapsize
, sizeof(*_nsmap
), _nsdbtcmp
);
654 srclist
= dbt
->srclist
;
655 srclistsize
= dbt
->srclistsize
;
659 while (srclist
[srclistsize
].name
!= NULL
)
664 for (i
= 0; i
< srclistsize
; i
++) {
665 cb
= _nsmethod(srclist
[i
].name
, database
, method
,
669 va_start(ap
, defaults
);
670 result
= (*cb
)(retval
, cb_data
, ap
);
672 if (defaults
[0].flags
& NS_FORCEALL
)
674 if (result
& srclist
[i
].flags
)
678 result
&= NS_STATUSMASK
; /* clear private flags in result */
680 rwlock_unlock(&_nslock
);
684 mutex_lock(&_ns_drec_lock
);
685 LIST_REMOVE(&drec
, list
);
686 mutex_unlock(&_ns_drec_lock
);
688 #endif /* _REENTRANT */
691 return (result
? result
: NS_NOTFOUND
);