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 <sys/types.h>
70 #include <sys/param.h>
72 #include <sys/queue.h>
90 #define __isthreaded 1
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 pthread_rwlock_t _nslock
;
157 static pthread_once_t _nslockinit
= PTHREAD_ONCE_INIT
;
159 static void lockinit()
161 int result
= pthread_rwlock_init(&_nslock
, NULL
);
166 * List of threads currently in nsdispatch(). We use this to detect
167 * recursive calls and avoid reloading configuration in such cases,
168 * which could cause deadlock.
171 LIST_ENTRY(_ns_drec
) list
;
174 static LIST_HEAD(, _ns_drec
) _ns_drec
= LIST_HEAD_INITIALIZER(&_ns_drec
);
175 static pthread_mutex_t _ns_drec_lock
= PTHREAD_MUTEX_INITIALIZER
;
176 #endif /* _REENTRANT */
180 * Runtime determination of whether we are dynamically linked or not.
182 #define is_dynamic() (0) /* don't bother - switch to ELF! */
186 * size of dynamic array chunk for _nsmap and _nsmap[x].srclist (and other
189 #define NSELEMSPERCHUNK 8
192 * Dynamically growable arrays are used for lists of databases, sources,
193 * and modules. The following "vector" API is used to isolate the
196 typedef void (*_nsvect_free_elem
)(void *);
199 _nsvect_append(const void *elem
, void *vec
, u_int
*count
, size_t esize
)
203 if ((*count
% NSELEMSPERCHUNK
) == 0) {
204 p
= realloc(vec
, (*count
+ NSELEMSPERCHUNK
) * esize
);
209 memmove((void *)(((uintptr_t)vec
) + (*count
* esize
)), elem
, esize
);
215 _nsvect_elem(u_int i
, void *vec
, u_int count
, size_t esize
)
219 return ((void *)((uintptr_t)vec
+ (i
* esize
)));
225 _nsvect_free(void *vec
, u_int
*count
, size_t esize
, _nsvect_free_elem free_elem
)
230 for (i
= 0; i
< *count
; i
++) {
231 elem
= _nsvect_elem(i
, vec
, *count
, esize
);
239 #define _NSVECT_FREE(v, c, s, f) \
241 _nsvect_free((v), (c), (s), (f)); \
243 } while (/*CONSTCOND*/0)
246 _nsdbtcmp(const void *a
, const void *b
)
249 return (strcasecmp(((const ns_dbt
*)a
)->name
,
250 ((const ns_dbt
*)b
)->name
));
254 _nsmodcmp(const void *a
, const void *b
)
257 return (strcasecmp(((const ns_mod
*)a
)->name
,
258 ((const ns_mod
*)b
)->name
));
262 _nsmtabcmp(const void *a
, const void *b
)
266 cmp
= strcmp(((const ns_mtab
*)a
)->name
,
267 ((const ns_mtab
*)b
)->name
);
271 return (strcasecmp(((const ns_mtab
*)a
)->database
,
272 ((const ns_mtab
*)b
)->database
));
276 _nsmodfree(ns_mod
*mod
)
279 free(__UNCONST(mod
->name
));
280 if (mod
->handle
== NULL
)
282 if (mod
->unregister
!= NULL
)
283 (*mod
->unregister
)(mod
->mtab
, mod
->mtabsize
);
285 if (mod
->handle
!= _nsbuiltin
)
286 (void) dlclose(mod
->handle
);
291 * Load a built-in or dyanamically linked module. If the `reg_fn'
292 * argument is non-NULL, assume a built-in module and use `reg_fn'
293 * to register it. Otherwise, search for a dynamic nsswitch module.
296 _nsloadmod(const char *source
, nss_module_register_fn reg_fn
)
303 memset(&mod
, 0, sizeof(mod
));
304 mod
.name
= strdup(source
);
305 if (mod
.name
== NULL
)
308 if (reg_fn
!= NULL
) {
310 * The placeholder is required, as a NULL handle
311 * represents an invalid module.
313 mod
.handle
= _nsbuiltin
;
314 } else if (!is_dynamic()) {
318 if (snprintf(buf
, sizeof(buf
), "nss_%s.so.%d", mod
.name
,
319 NSS_MODULE_INTERFACE_VERSION
) >= (int)sizeof(buf
))
321 mod
.handle
= dlopen(buf
, RTLD_LOCAL
| RTLD_LAZY
);
322 if (mod
.handle
== NULL
) {
325 * This gets pretty annoying, since the built-in
326 * sources are not yet modules.
328 /* XXX log some error? */
332 reg_fn
= (nss_module_register_fn
) dlsym(mod
.handle
,
333 "nss_module_register");
334 if (reg_fn
== NULL
) {
335 (void) dlclose(mod
.handle
);
337 /* XXX log some error? */
340 #else /* ! __ELF__ */
344 mod
.mtab
= (*reg_fn
)(mod
.name
, &mod
.mtabsize
, &mod
.unregister
);
345 if (mod
.mtab
== NULL
|| mod
.mtabsize
== 0) {
347 if (mod
.handle
!= _nsbuiltin
)
348 (void) dlclose(mod
.handle
);
351 /* XXX log some error? */
354 if (mod
.mtabsize
> 1)
355 qsort(mod
.mtab
, mod
.mtabsize
, sizeof(mod
.mtab
[0]),
358 new = _nsvect_append(&mod
, _nsmod
, &_nsmodsize
, sizeof(*_nsmod
));
364 /* _nsmodsize already incremented */
366 qsort(_nsmod
, _nsmodsize
, sizeof(*_nsmod
), _nsmodcmp
);
374 /* Do nothing, for now. */
378 _nsdbtaddsrc(ns_dbt
*dbt
, const ns_src
*src
)
387 new = _nsvect_append(src
, dbt
->srclist
, &dbt
->srclistsize
,
392 /* dbt->srclistsize already incremented */
394 modkey
.name
= src
->name
;
395 mod
= bsearch(&modkey
, _nsmod
, _nsmodsize
, sizeof(*_nsmod
), _nsmodcmp
);
397 return (_nsloadmod(src
->name
, NULL
));
403 _nsdbtdump(const ns_dbt
*dbt
)
409 printf("%s (%d source%s):", dbt
->name
, dbt
->srclistsize
,
410 dbt
->srclistsize
== 1 ? "" : "s");
411 for (i
= 0; i
< dbt
->srclistsize
; i
++) {
412 printf(" %s", dbt
->srclist
[i
].name
);
413 if (!(dbt
->srclist
[i
].flags
&
414 (NS_UNAVAIL
|NS_NOTFOUND
|NS_TRYAGAIN
)) &&
415 (dbt
->srclist
[i
].flags
& NS_SUCCESS
))
418 if (!(dbt
->srclist
[i
].flags
& NS_SUCCESS
))
419 printf(" SUCCESS=continue");
420 if (dbt
->srclist
[i
].flags
& NS_UNAVAIL
)
421 printf(" UNAVAIL=return");
422 if (dbt
->srclist
[i
].flags
& NS_NOTFOUND
)
423 printf(" NOTFOUND=return");
424 if (dbt
->srclist
[i
].flags
& NS_TRYAGAIN
)
425 printf(" TRYAGAIN=return");
432 _nssrclist_free(ns_src
**src
, u_int srclistsize
)
436 for (i
= 0; i
< srclistsize
; i
++) {
437 if ((*src
)[i
].name
!= NULL
)
438 free(__UNCONST((*src
)[i
].name
));
445 _nsdbtfree(ns_dbt
*dbt
)
448 _nssrclist_free(&dbt
->srclist
, dbt
->srclistsize
);
449 if (dbt
->name
!= NULL
)
450 free(__UNCONST(dbt
->name
));
454 _nsdbtput(const ns_dbt
*dbt
)
462 for (i
= 0; i
< _nsmapsize
; i
++) {
463 p
= _nsvect_elem(i
, _nsmap
, _nsmapsize
, sizeof(*_nsmap
));
464 if (strcasecmp(dbt
->name
, p
->name
) == 0) {
465 /* overwrite existing entry */
466 if (p
->srclist
!= NULL
)
467 _nssrclist_free(&p
->srclist
, p
->srclistsize
);
468 memmove(p
, dbt
, sizeof(*dbt
));
472 new = _nsvect_append(dbt
, _nsmap
, &_nsmapsize
, sizeof(*_nsmap
));
476 /* _nsmapsize already incremented */
482 * This function is called each time nsdispatch() is called. If this
483 * is the first call, or if the configuration has changed, (re-)prepare
484 * the global data used by NSS.
490 static pthread_mutex_t _nsconflock
= PTHREAD_MUTEX_INITIALIZER
;
492 static time_t _nsconfmod
;
495 pthread_mutex_lock(&_nsconflock
);
497 if (stat(_PATH_NS_CONF
, &statbuf
) == -1) {
499 * No nsswitch.conf; just use whatever configuration we
500 * currently have, or fall back on the defaults specified
503 pthread_mutex_unlock(&_nsconflock
);
507 if (statbuf
.st_mtime
<= _nsconfmod
) {
508 /* Internal state is up-to-date with nsswitch.conf. */
509 pthread_mutex_unlock(&_nsconflock
);
514 * Ok, we've decided we need to update the nsswitch configuration
515 * structures. Acquire a write-lock on _nslock while continuing
516 * to hold _nsconflock. Acquiring a write-lock blocks while
517 * waiting for other threads already holding a read-lock to clear.
518 * We hold _nsconflock for the duration, and update the time stamp
519 * at the end of the update operation, at which time we release
522 pthread_rwlock_wrlock(&_nslock
);
524 _nsyyin
= fopen(_PATH_NS_CONF
, "r");
525 if (_nsyyin
== NULL
) {
527 * Unable to open nsswitch.conf; behave as though the
528 * stat() above failed. Even though we have already
529 * updated _nsconfmod, if the file reappears, the
535 _NSVECT_FREE(_nsmap
, &_nsmapsize
, sizeof(*_nsmap
),
536 (_nsvect_free_elem
) _nsdbtfree
);
537 _NSVECT_FREE(_nsmod
, &_nsmodsize
, sizeof(*_nsmod
),
538 (_nsvect_free_elem
) _nsmodfree
);
543 (void) fclose(_nsyyin
);
545 qsort(_nsmap
, _nsmapsize
, sizeof(*_nsmap
), _nsdbtcmp
);
547 _nsconfmod
= statbuf
.st_mtime
;
550 pthread_rwlock_unlock(&_nslock
);
551 pthread_mutex_unlock(&_nsconflock
);
556 _nsmethod(const char *source
, const char *database
, const char *method
,
557 const ns_dtab disp_tab
[], void **cb_data
)
561 ns_mtab
*mtab
, mtabkey
;
563 if (disp_tab
!= NULL
) {
564 for (curdisp
= 0; disp_tab
[curdisp
].src
!= NULL
; curdisp
++) {
565 if (strcasecmp(source
, disp_tab
[curdisp
].src
) == 0) {
566 *cb_data
= disp_tab
[curdisp
].cb_data
;
567 return (disp_tab
[curdisp
].callback
);
572 modkey
.name
= source
;
573 mod
= bsearch(&modkey
, _nsmod
, _nsmodsize
, sizeof(*_nsmod
),
575 if (mod
!= NULL
&& mod
->handle
!= NULL
) {
576 mtabkey
.database
= database
;
577 mtabkey
.name
= method
;
578 mtab
= bsearch(&mtabkey
, mod
->mtab
, mod
->mtabsize
,
579 sizeof(mod
->mtab
[0]), _nsmtabcmp
);
581 *cb_data
= mtab
->mdata
;
582 return (mtab
->method
);
592 nsdispatch(void *retval
, const ns_dtab disp_tab
[], const char *database
,
593 const char *method
, const ns_src defaults
[], ...)
595 static int _nsdispatching
;
597 struct _ns_drec drec
, *ldrec
;
603 const ns_src
*srclist
;
608 /* retval may be NULL */
609 /* disp_tab may be NULL */
610 assert(database
!= NULL
);
611 assert(method
!= NULL
);
612 assert(defaults
!= NULL
);
613 if (database
== NULL
|| method
== NULL
|| defaults
== NULL
)
616 pthread_once(&_nslockinit
, lockinit
);
619 * In both the threaded and non-threaded cases, avoid reloading
620 * the configuration if the current thread is already running
621 * nsdispatch() (i.e. recursive call).
623 * In the non-threaded case, this avoids changing the data structures
624 * while we're using them.
626 * In the threaded case, this avoids trying to take a write lock
627 * while the current thread holds a read lock (which would result
632 drec
.thr
= pthread_self();
633 pthread_mutex_lock(&_ns_drec_lock
);
634 LIST_FOREACH(ldrec
, &_ns_drec
, list
) {
635 if (ldrec
->thr
== drec
.thr
)
638 LIST_INSERT_HEAD(&_ns_drec
, &drec
, list
);
639 pthread_mutex_unlock(&_ns_drec_lock
);
640 if (ldrec
== NULL
&& _nsconfigure()) {
641 pthread_mutex_lock(&_ns_drec_lock
);
642 LIST_REMOVE(&drec
, list
);
643 pthread_mutex_unlock(&_ns_drec_lock
);
647 #endif /* _REENTRANT */
649 if (_nsdispatching
++ == 0 && _nsconfigure()) {
654 pthread_rwlock_rdlock(&_nslock
);
657 dbt
= bsearch(&key
, _nsmap
, _nsmapsize
, sizeof(*_nsmap
), _nsdbtcmp
);
659 srclist
= dbt
->srclist
;
660 srclistsize
= dbt
->srclistsize
;
664 while (srclist
[srclistsize
].name
!= NULL
)
669 for (i
= 0; i
< srclistsize
; i
++) {
670 cb
= _nsmethod(srclist
[i
].name
, database
, method
,
674 va_start(ap
, defaults
);
675 result
= (*cb
)(retval
, cb_data
, ap
);
677 if (defaults
[0].flags
& NS_FORCEALL
)
679 if (result
& srclist
[i
].flags
)
683 result
&= NS_STATUSMASK
; /* clear private flags in result */
685 pthread_rwlock_unlock(&_nslock
);
689 pthread_mutex_lock(&_ns_drec_lock
);
690 LIST_REMOVE(&drec
, list
);
691 pthread_mutex_unlock(&_ns_drec_lock
);
693 #endif /* _REENTRANT */
696 return (result
? result
: NS_NOTFOUND
);