1 /* $NetBSD: nsdispatch.c,v 1.34 2009/02/05 13:21:11 lukem 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.34 2009/02/05 13:21:11 lukem 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 */
172 #define is_dynamic() (0) /* don't bother - switch to ELF! */
173 #elif __GNUC_PREREQ__(4,2)
174 static int rtld_DYNAMIC
__attribute__((__weakref__
, __alias__("_DYNAMIC")));
175 #define is_dynamic() (&rtld_DYNAMIC != NULL)
177 extern int _DYNAMIC
__weak_reference(_DYNAMIC
);
178 #define is_dynamic() (&_DYNAMIC != NULL)
182 * size of dynamic array chunk for _nsmap and _nsmap[x].srclist (and other
185 #define NSELEMSPERCHUNK 8
188 * Dynamically growable arrays are used for lists of databases, sources,
189 * and modules. The following "vector" API is used to isolate the
192 typedef void (*_nsvect_free_elem
)(void *);
195 _nsvect_append(const void *elem
, void *vec
, u_int
*count
, size_t esize
)
199 if ((*count
% NSELEMSPERCHUNK
) == 0) {
200 p
= realloc(vec
, (*count
+ NSELEMSPERCHUNK
) * esize
);
205 memmove((void *)(((uintptr_t)vec
) + (*count
* esize
)), elem
, esize
);
211 _nsvect_elem(u_int i
, void *vec
, u_int count
, size_t esize
)
215 return ((void *)((uintptr_t)vec
+ (i
* esize
)));
221 _nsvect_free(void *vec
, u_int
*count
, size_t esize
, _nsvect_free_elem free_elem
)
226 for (i
= 0; i
< *count
; i
++) {
227 elem
= _nsvect_elem(i
, vec
, *count
, esize
);
235 #define _NSVECT_FREE(v, c, s, f) \
237 _nsvect_free((v), (c), (s), (f)); \
239 } while (/*CONSTCOND*/0)
242 _nsdbtcmp(const void *a
, const void *b
)
245 return (strcasecmp(((const ns_dbt
*)a
)->name
,
246 ((const ns_dbt
*)b
)->name
));
250 _nsmodcmp(const void *a
, const void *b
)
253 return (strcasecmp(((const ns_mod
*)a
)->name
,
254 ((const ns_mod
*)b
)->name
));
258 _nsmtabcmp(const void *a
, const void *b
)
262 cmp
= strcmp(((const ns_mtab
*)a
)->name
,
263 ((const ns_mtab
*)b
)->name
);
267 return (strcasecmp(((const ns_mtab
*)a
)->database
,
268 ((const ns_mtab
*)b
)->database
));
272 _nsmodfree(ns_mod
*mod
)
275 free(__UNCONST(mod
->name
));
276 if (mod
->handle
== NULL
)
278 if (mod
->unregister
!= NULL
)
279 (*mod
->unregister
)(mod
->mtab
, mod
->mtabsize
);
281 if (mod
->handle
!= _nsbuiltin
)
282 (void) dlclose(mod
->handle
);
287 * Load a built-in or dyanamically linked module. If the `reg_fn'
288 * argument is non-NULL, assume a built-in module and use `reg_fn'
289 * to register it. Otherwise, search for a dynamic nsswitch module.
292 _nsloadmod(const char *source
, nss_module_register_fn reg_fn
)
299 memset(&mod
, 0, sizeof(mod
));
300 mod
.name
= strdup(source
);
301 if (mod
.name
== NULL
)
304 if (reg_fn
!= NULL
) {
306 * The placeholder is required, as a NULL handle
307 * represents an invalid module.
309 mod
.handle
= _nsbuiltin
;
310 } else if (!is_dynamic()) {
314 if (snprintf(buf
, sizeof(buf
), "nss_%s.so.%d", mod
.name
,
315 NSS_MODULE_INTERFACE_VERSION
) >= (int)sizeof(buf
))
317 mod
.handle
= dlopen(buf
, RTLD_LOCAL
| RTLD_LAZY
);
318 if (mod
.handle
== NULL
) {
321 * This gets pretty annoying, since the built-in
322 * sources are not yet modules.
324 /* XXX log some error? */
328 reg_fn
= (nss_module_register_fn
) dlsym(mod
.handle
,
329 "nss_module_register");
330 if (reg_fn
== NULL
) {
331 (void) dlclose(mod
.handle
);
333 /* XXX log some error? */
336 #else /* ! __ELF__ */
340 mod
.mtab
= (*reg_fn
)(mod
.name
, &mod
.mtabsize
, &mod
.unregister
);
341 if (mod
.mtab
== NULL
|| mod
.mtabsize
== 0) {
343 if (mod
.handle
!= _nsbuiltin
)
344 (void) dlclose(mod
.handle
);
347 /* XXX log some error? */
350 if (mod
.mtabsize
> 1)
351 qsort(mod
.mtab
, mod
.mtabsize
, sizeof(mod
.mtab
[0]),
354 new = _nsvect_append(&mod
, _nsmod
, &_nsmodsize
, sizeof(*_nsmod
));
360 /* _nsmodsize already incremented */
362 qsort(_nsmod
, _nsmodsize
, sizeof(*_nsmod
), _nsmodcmp
);
370 /* Do nothing, for now. */
374 _nsdbtaddsrc(ns_dbt
*dbt
, const ns_src
*src
)
380 _DIAGASSERT(dbt
!= NULL
);
381 _DIAGASSERT(src
!= NULL
);
383 new = _nsvect_append(src
, dbt
->srclist
, &dbt
->srclistsize
,
388 /* dbt->srclistsize already incremented */
390 modkey
.name
= src
->name
;
391 mod
= bsearch(&modkey
, _nsmod
, _nsmodsize
, sizeof(*_nsmod
),
394 return (_nsloadmod(src
->name
, NULL
));
400 _nsdbtdump(const ns_dbt
*dbt
)
404 _DIAGASSERT(dbt
!= NULL
);
406 printf("%s (%d source%s):", dbt
->name
, dbt
->srclistsize
,
407 dbt
->srclistsize
== 1 ? "" : "s");
408 for (i
= 0; i
< dbt
->srclistsize
; i
++) {
409 printf(" %s", dbt
->srclist
[i
].name
);
410 if (!(dbt
->srclist
[i
].flags
&
411 (NS_UNAVAIL
|NS_NOTFOUND
|NS_TRYAGAIN
)) &&
412 (dbt
->srclist
[i
].flags
& NS_SUCCESS
))
415 if (!(dbt
->srclist
[i
].flags
& NS_SUCCESS
))
416 printf(" SUCCESS=continue");
417 if (dbt
->srclist
[i
].flags
& NS_UNAVAIL
)
418 printf(" UNAVAIL=return");
419 if (dbt
->srclist
[i
].flags
& NS_NOTFOUND
)
420 printf(" NOTFOUND=return");
421 if (dbt
->srclist
[i
].flags
& NS_TRYAGAIN
)
422 printf(" TRYAGAIN=return");
429 _nssrclist_free(ns_src
**src
, u_int srclistsize
)
433 for (i
= 0; i
< srclistsize
; i
++) {
434 if ((*src
)[i
].name
!= NULL
)
435 free(__UNCONST((*src
)[i
].name
));
442 _nsdbtfree(ns_dbt
*dbt
)
445 _nssrclist_free(&dbt
->srclist
, dbt
->srclistsize
);
446 if (dbt
->name
!= NULL
)
447 free(__UNCONST(dbt
->name
));
451 _nsdbtput(const ns_dbt
*dbt
)
457 _DIAGASSERT(dbt
!= NULL
);
459 for (i
= 0; i
< _nsmapsize
; i
++) {
460 p
= _nsvect_elem(i
, _nsmap
, _nsmapsize
, sizeof(*_nsmap
));
461 if (strcasecmp(dbt
->name
, p
->name
) == 0) {
462 /* overwrite existing entry */
463 if (p
->srclist
!= NULL
)
464 _nssrclist_free(&p
->srclist
, p
->srclistsize
);
465 memmove(p
, dbt
, sizeof(*dbt
));
469 new = _nsvect_append(dbt
, _nsmap
, &_nsmapsize
, sizeof(*_nsmap
));
473 /* _nsmapsize already incremented */
479 * This function is called each time nsdispatch() is called. If this
480 * is the first call, or if the configuration has changed, (re-)prepare
481 * the global data used by NSS.
487 static mutex_t _nsconflock
= MUTEX_INITIALIZER
;
489 static time_t _nsconfmod
;
492 mutex_lock(&_nsconflock
);
494 if (stat(_PATH_NS_CONF
, &statbuf
) == -1) {
496 * No nsswitch.conf; just use whatever configuration we
497 * currently have, or fall back on the defaults specified
500 mutex_unlock(&_nsconflock
);
504 if (statbuf
.st_mtime
<= _nsconfmod
) {
505 /* Internal state is up-to-date with nsswitch.conf. */
506 mutex_unlock(&_nsconflock
);
511 * Ok, we've decided we need to update the nsswitch configuration
512 * structures. Acquire a write-lock on _nslock while continuing
513 * to hold _nsconflock. Acquiring a write-lock blocks while
514 * waiting for other threads already holding a read-lock to clear.
515 * We hold _nsconflock for the duration, and update the time stamp
516 * at the end of the update operation, at which time we release
519 rwlock_wrlock(&_nslock
);
521 _nsyyin
= fopen(_PATH_NS_CONF
, "r");
522 if (_nsyyin
== NULL
) {
524 * Unable to open nsswitch.conf; behave as though the
525 * stat() above failed. Even though we have already
526 * updated _nsconfmod, if the file reappears, the
532 _NSVECT_FREE(_nsmap
, &_nsmapsize
, sizeof(*_nsmap
),
533 (_nsvect_free_elem
) _nsdbtfree
);
534 _NSVECT_FREE(_nsmod
, &_nsmodsize
, sizeof(*_nsmod
),
535 (_nsvect_free_elem
) _nsmodfree
);
540 (void) fclose(_nsyyin
);
542 qsort(_nsmap
, _nsmapsize
, sizeof(*_nsmap
), _nsdbtcmp
);
544 _nsconfmod
= statbuf
.st_mtime
;
547 rwlock_unlock(&_nslock
);
548 mutex_unlock(&_nsconflock
);
553 _nsmethod(const char *source
, const char *database
, const char *method
,
554 const ns_dtab disp_tab
[], void **cb_data
)
558 ns_mtab
*mtab
, mtabkey
;
560 if (disp_tab
!= NULL
) {
561 for (curdisp
= 0; disp_tab
[curdisp
].src
!= NULL
; curdisp
++) {
562 if (strcasecmp(source
, disp_tab
[curdisp
].src
) == 0) {
563 *cb_data
= disp_tab
[curdisp
].cb_data
;
564 return (disp_tab
[curdisp
].callback
);
569 modkey
.name
= source
;
570 mod
= bsearch(&modkey
, _nsmod
, _nsmodsize
, sizeof(*_nsmod
),
572 if (mod
!= NULL
&& mod
->handle
!= NULL
) {
573 mtabkey
.database
= database
;
574 mtabkey
.name
= method
;
575 mtab
= bsearch(&mtabkey
, mod
->mtab
, mod
->mtabsize
,
576 sizeof(mod
->mtab
[0]), _nsmtabcmp
);
578 *cb_data
= mtab
->mdata
;
579 return (mtab
->method
);
589 nsdispatch(void *retval
, const ns_dtab disp_tab
[], const char *database
,
590 const char *method
, const ns_src defaults
[], ...)
592 static int _nsdispatching
;
594 struct _ns_drec drec
, *ldrec
;
600 const ns_src
*srclist
;
605 /* retval may be NULL */
606 /* disp_tab may be NULL */
607 _DIAGASSERT(database
!= NULL
);
608 _DIAGASSERT(method
!= NULL
);
609 _DIAGASSERT(defaults
!= NULL
);
610 if (database
== NULL
|| method
== NULL
|| defaults
== NULL
)
614 * In both the threaded and non-threaded cases, avoid reloading
615 * the configuration if the current thread is already running
616 * nsdispatch() (i.e. recursive call).
618 * In the non-threaded case, this avoids changing the data structures
619 * while we're using them.
621 * In the threaded case, this avoids trying to take a write lock
622 * while the current thread holds a read lock (which would result
627 drec
.thr
= thr_self();
628 mutex_lock(&_ns_drec_lock
);
629 LIST_FOREACH(ldrec
, &_ns_drec
, list
) {
630 if (ldrec
->thr
== drec
.thr
)
633 LIST_INSERT_HEAD(&_ns_drec
, &drec
, list
);
634 mutex_unlock(&_ns_drec_lock
);
635 if (ldrec
== NULL
&& _nsconfigure()) {
636 mutex_lock(&_ns_drec_lock
);
637 LIST_REMOVE(&drec
, list
);
638 mutex_unlock(&_ns_drec_lock
);
642 #endif /* _REENTRANT */
643 if (_nsdispatching
++ == 0 && _nsconfigure()) {
648 rwlock_rdlock(&_nslock
);
651 dbt
= bsearch(&key
, _nsmap
, _nsmapsize
, sizeof(*_nsmap
), _nsdbtcmp
);
653 srclist
= dbt
->srclist
;
654 srclistsize
= dbt
->srclistsize
;
658 while (srclist
[srclistsize
].name
!= NULL
)
663 for (i
= 0; i
< srclistsize
; i
++) {
664 cb
= _nsmethod(srclist
[i
].name
, database
, method
,
668 va_start(ap
, defaults
);
669 result
= (*cb
)(retval
, cb_data
, ap
);
671 if (defaults
[0].flags
& NS_FORCEALL
)
673 if (result
& srclist
[i
].flags
)
677 result
&= NS_STATUSMASK
; /* clear private flags in result */
679 rwlock_unlock(&_nslock
);
683 mutex_lock(&_ns_drec_lock
);
684 LIST_REMOVE(&drec
, list
);
685 mutex_unlock(&_ns_drec_lock
);
687 #endif /* _REENTRANT */
690 return (result
? result
: NS_NOTFOUND
);