1 /* $NetBSD: nsdispatch.c,v 1.30 2005/11/29 03:11:59 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1997, 1998, 1999, 2004 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn; and by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 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. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /*- 40 * Copyright (c) 2003 Networks Associates Technology, Inc. 41 * All rights reserved. 42 * 43 * Portions of this software were developed for the FreeBSD Project by 44 * Jacques A. Vidrine, Safeport Network Services, and Network 45 * Associates Laboratories, the Security Research Division of Network 46 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 47 * ("CBOSS"), as part of the DARPA CHATS research program. 48 * 49 * Redistribution and use in source and binary forms, with or without 50 * modification, are permitted provided that the following conditions 51 * are met: 52 * 1. Redistributions of source code must retain the above copyright 53 * notice, this list of conditions and the following disclaimer. 54 * 2. Redistributions in binary form must reproduce the above copyright 55 * notice, this list of conditions and the following disclaimer in the 56 * documentation and/or other materials provided with the distribution. 57 * 58 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 61 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 68 * SUCH DAMAGE. 69 */ 70 71 #include <sys/cdefs.h> 72 #if defined(LIBC_SCCS) && !defined(lint) 73 __RCSID("$NetBSD: nsdispatch.c,v 1.30 2005/11/29 03:11:59 christos Exp $"); 74 #endif /* LIBC_SCCS and not lint */ 75 76 #include "namespace.h" 77 78 #include <sys/types.h> 79 #include <sys/param.h> 80 #include <sys/stat.h> 81 #include <sys/queue.h> 82 83 #include <assert.h> 84 #ifdef __ELF__ 85 #include <dlfcn.h> 86 #endif /* __ELF__ */ 87 #include <err.h> 88 #include <fcntl.h> 89 #define _NS_PRIVATE 90 #include <nsswitch.h> 91 #include <stdarg.h> 92 #include <stdio.h> 93 #include <stdlib.h> 94 #include <string.h> 95 #include <unistd.h> 96 97 #include "reentrant.h" 98 99 extern FILE *_nsyyin; 100 extern int _nsyyparse(void); 101 102 103 #ifdef __weak_alias 104 __weak_alias(nsdispatch,_nsdispatch) 105 #endif 106 107 108 /* 109 * default sourcelist: `files' 110 */ 111 const ns_src __nsdefaultsrc[] = { 112 { NSSRC_FILES, NS_SUCCESS }, 113 { 0 }, 114 }; 115 116 const ns_src __nsdefaultcompat[] = { 117 { NSSRC_COMPAT, NS_SUCCESS }, 118 { 0 } 119 }; 120 121 const ns_src __nsdefaultcompat_forceall[] = { 122 { NSSRC_COMPAT, NS_SUCCESS | NS_FORCEALL }, 123 { 0 } 124 }; 125 126 const ns_src __nsdefaultfiles[] = { 127 { NSSRC_FILES, NS_SUCCESS }, 128 { 0 }, 129 }; 130 131 const ns_src __nsdefaultfiles_forceall[] = { 132 { NSSRC_FILES, NS_SUCCESS | NS_FORCEALL }, 133 { 0 }, 134 }; 135 136 const ns_src __nsdefaultnis[] = { 137 { NSSRC_NIS, NS_SUCCESS }, 138 { 0 } 139 }; 140 141 const ns_src __nsdefaultnis_forceall[] = { 142 { NSSRC_NIS, NS_SUCCESS | NS_FORCEALL }, 143 { 0 } 144 }; 145 146 147 /* Database, source mappings. */ 148 static u_int _nsmapsize; 149 static ns_dbt *_nsmap; 150 151 /* Nsswitch modules. */ 152 static u_int _nsmodsize; 153 static ns_mod *_nsmod; 154 155 /* Placeholder for built-in modules' dlopen() handles. */ 156 static void *_nsbuiltin = &_nsbuiltin; 157 158 #ifdef _REENTRANT 159 /* 160 * Global nsswitch data structures are mostly read-only, but we update them 161 * when we read or re-read nsswitch.conf. 162 */ 163 static rwlock_t _nslock = RWLOCK_INITIALIZER; 164 165 /* 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. 169 */ 170 struct _ns_drec { 171 LIST_ENTRY(_ns_drec) list; 172 thr_t thr; 173 }; 174 static LIST_HEAD(, _ns_drec) _ns_drec = LIST_HEAD_INITIALIZER(&_ns_drec); 175 static mutex_t _ns_drec_lock = MUTEX_INITIALIZER; 176 #endif /* _REENTRANT */ 177 178 179 /* 180 * Runtime determination of whether we are dynamically linked or not. 181 */ 182 #ifdef __ELF__ 183 extern int _DYNAMIC __attribute__((__weak__)); 184 #define is_dynamic() (&_DYNAMIC != NULL) 185 #else 186 #define is_dynamic() (0) /* don't bother - switch to ELF! */ 187 #endif /* __ELF__ */ 188 189 190 /* 191 * size of dynamic array chunk for _nsmap and _nsmap[x].srclist (and other 192 * growing arrays). 193 */ 194 #define NSELEMSPERCHUNK 8 195 196 /* 197 * Dynamically growable arrays are used for lists of databases, sources, 198 * and modules. The following "vector" API is used to isolate the 199 * common operations. 200 */ 201 typedef void (*_nsvect_free_elem)(void *); 202 203 static void * 204 _nsvect_append(const void *elem, void *vec, u_int *count, size_t esize) 205 { 206 void *p; 207 208 if ((*count % NSELEMSPERCHUNK) == 0) { 209 p = realloc(vec, (*count + NSELEMSPERCHUNK) * esize); 210 if (p == NULL) 211 return (NULL); 212 vec = p; 213 } 214 memmove((void *)(((uintptr_t)vec) + (*count * esize)), elem, esize); 215 (*count)++; 216 return (vec); 217 } 218 219 static void * 220 _nsvect_elem(u_int i, void *vec, u_int count, size_t esize) 221 { 222 223 if (i < count) 224 return ((void *)((uintptr_t)vec + (i * esize))); 225 else 226 return (NULL); 227 } 228 229 static void 230 _nsvect_free(void *vec, u_int *count, size_t esize, _nsvect_free_elem free_elem) 231 { 232 void *elem; 233 u_int i; 234 235 for (i = 0; i < *count; i++) { 236 elem = _nsvect_elem(i, vec, *count, esize); 237 if (elem != NULL) 238 (*free_elem)(elem); 239 } 240 if (vec != NULL) 241 free(vec); 242 *count = 0; 243 } 244 #define _NSVECT_FREE(v, c, s, f) \ 245 do { \ 246 _nsvect_free((v), (c), (s), (f)); \ 247 (v) = NULL; \ 248 } while (/*CONSTCOND*/0) 249 250 static int 251 _nsdbtcmp(const void *a, const void *b) 252 { 253 254 return (strcasecmp(((const ns_dbt *)a)->name, 255 ((const ns_dbt *)b)->name)); 256 } 257 258 static int 259 _nsmodcmp(const void *a, const void *b) 260 { 261 262 return (strcasecmp(((const ns_mod *)a)->name, 263 ((const ns_mod *)b)->name)); 264 } 265 266 static int 267 _nsmtabcmp(const void *a, const void *b) 268 { 269 int cmp; 270 271 cmp = strcmp(((const ns_mtab *)a)->name, 272 ((const ns_mtab *)b)->name); 273 if (cmp) 274 return (cmp); 275 276 return (strcasecmp(((const ns_mtab *)a)->database, 277 ((const ns_mtab *)b)->database)); 278 } 279 280 static void 281 _nsmodfree(ns_mod *mod) 282 { 283 284 free(__UNCONST(mod->name)); 285 if (mod->handle == NULL) 286 return; 287 if (mod->unregister != NULL) 288 (*mod->unregister)(mod->mtab, mod->mtabsize); 289 #ifdef __ELF__ 290 if (mod->handle != _nsbuiltin) 291 (void) dlclose(mod->handle); 292 #endif /* __ELF__ */ 293 } 294 295 /* 296 * Load a built-in or dyanamically linked module. If the `reg_fn' 297 * argument is non-NULL, assume a built-in module and use `reg_fn' 298 * to register it. Otherwise, search for a dynamic nsswitch module. 299 */ 300 static int 301 _nsloadmod(const char *source, nss_module_register_fn reg_fn) 302 { 303 #ifdef __ELF__ 304 char buf[PATH_MAX]; 305 #endif 306 ns_mod mod, *new; 307 308 memset(&mod, 0, sizeof(mod)); 309 mod.name = strdup(source); 310 if (mod.name == NULL) 311 return (-1); 312 313 if (reg_fn != NULL) { 314 /* 315 * The placeholder is required, as a NULL handle 316 * represents an invalid module. 317 */ 318 mod.handle = _nsbuiltin; 319 } else if (!is_dynamic()) { 320 goto out; 321 } else { 322 #ifdef __ELF__ 323 if (snprintf(buf, sizeof(buf), "nss_%s.so.%d", mod.name, 324 NSS_MODULE_INTERFACE_VERSION) >= (int)sizeof(buf)) 325 goto out; 326 mod.handle = dlopen(buf, RTLD_LOCAL | RTLD_LAZY); 327 if (mod.handle == NULL) { 328 #ifdef _NSS_DEBUG 329 /* 330 * This gets pretty annoying, since the built-in 331 * sources are not yet modules. 332 */ 333 /* XXX log some error? */ 334 #endif 335 goto out; 336 } 337 reg_fn = (nss_module_register_fn) dlsym(mod.handle, 338 "nss_module_register"); 339 if (reg_fn == NULL) { 340 (void) dlclose(mod.handle); 341 mod.handle = NULL; 342 /* XXX log some error? */ 343 goto out; 344 } 345 #else /* ! __ELF__ */ 346 mod.handle = NULL; 347 #endif /* __ELF__ */ 348 } 349 mod.mtab = (*reg_fn)(mod.name, &mod.mtabsize, &mod.unregister); 350 if (mod.mtab == NULL || mod.mtabsize == 0) { 351 #ifdef __ELF__ 352 if (mod.handle != _nsbuiltin) 353 (void) dlclose(mod.handle); 354 #endif /* __ELF__ */ 355 mod.handle = NULL; 356 /* XXX log some error? */ 357 goto out; 358 } 359 if (mod.mtabsize > 1) 360 qsort(mod.mtab, mod.mtabsize, sizeof(mod.mtab[0]), 361 _nsmtabcmp); 362 out: 363 new = _nsvect_append(&mod, _nsmod, &_nsmodsize, sizeof(*_nsmod)); 364 if (new == NULL) { 365 _nsmodfree(&mod); 366 return (-1); 367 } 368 _nsmod = new; 369 /* _nsmodsize already incremented */ 370 371 qsort(_nsmod, _nsmodsize, sizeof(*_nsmod), _nsmodcmp); 372 return (0); 373 } 374 375 static void 376 _nsloadbuiltin(void) 377 { 378 379 /* Do nothing, for now. */ 380 } 381 382 int 383 _nsdbtaddsrc(ns_dbt *dbt, const ns_src *src) 384 { 385 void *new; 386 const ns_mod *mod; 387 ns_mod modkey; 388 389 _DIAGASSERT(dbt != NULL); 390 _DIAGASSERT(src != NULL); 391 392 new = _nsvect_append(src, dbt->srclist, &dbt->srclistsize, 393 sizeof(*src)); 394 if (new == NULL) 395 return (-1); 396 dbt->srclist = new; 397 /* dbt->srclistsize already incremented */ 398 399 modkey.name = src->name; 400 mod = bsearch(&modkey, _nsmod, _nsmodsize, sizeof(*_nsmod), 401 _nsmodcmp); 402 if (mod == NULL) 403 return (_nsloadmod(src->name, NULL)); 404 405 return (0); 406 } 407 408 void 409 _nsdbtdump(const ns_dbt *dbt) 410 { 411 int i; 412 413 _DIAGASSERT(dbt != NULL); 414 415 printf("%s (%d source%s):", dbt->name, dbt->srclistsize, 416 dbt->srclistsize == 1 ? "" : "s"); 417 for (i = 0; i < dbt->srclistsize; i++) { 418 printf(" %s", dbt->srclist[i].name); 419 if (!(dbt->srclist[i].flags & 420 (NS_UNAVAIL|NS_NOTFOUND|NS_TRYAGAIN)) && 421 (dbt->srclist[i].flags & NS_SUCCESS)) 422 continue; 423 printf(" ["); 424 if (!(dbt->srclist[i].flags & NS_SUCCESS)) 425 printf(" SUCCESS=continue"); 426 if (dbt->srclist[i].flags & NS_UNAVAIL) 427 printf(" UNAVAIL=return"); 428 if (dbt->srclist[i].flags & NS_NOTFOUND) 429 printf(" NOTFOUND=return"); 430 if (dbt->srclist[i].flags & NS_TRYAGAIN) 431 printf(" TRYAGAIN=return"); 432 printf(" ]"); 433 } 434 printf("\n"); 435 } 436 437 static void 438 _nssrclist_free(ns_src **src, u_int srclistsize) 439 { 440 u_int i; 441 442 for (i = 0; i < srclistsize; i++) { 443 if ((*src)[i].name != NULL) 444 free(__UNCONST((*src)[i].name)); 445 } 446 free(*src); 447 *src = NULL; 448 } 449 450 static void 451 _nsdbtfree(ns_dbt *dbt) 452 { 453 454 _nssrclist_free(&dbt->srclist, dbt->srclistsize); 455 if (dbt->name != NULL) 456 free(__UNCONST(dbt->name)); 457 } 458 459 int 460 _nsdbtput(const ns_dbt *dbt) 461 { 462 ns_dbt *p; 463 void *new; 464 u_int i; 465 466 _DIAGASSERT(dbt != NULL); 467 468 for (i = 0; i < _nsmapsize; i++) { 469 p = _nsvect_elem(i, _nsmap, _nsmapsize, sizeof(*_nsmap)); 470 if (strcasecmp(dbt->name, p->name) == 0) { 471 /* overwrite existing entry */ 472 if (p->srclist != NULL) 473 _nssrclist_free(&p->srclist, p->srclistsize); 474 memmove(p, dbt, sizeof(*dbt)); 475 return (0); 476 } 477 } 478 new = _nsvect_append(dbt, _nsmap, &_nsmapsize, sizeof(*_nsmap)); 479 if (new == NULL) 480 return (-1); 481 _nsmap = new; 482 /* _nsmapsize already incremented */ 483 484 return (0); 485 } 486 487 /* 488 * This function is called each time nsdispatch() is called. If this 489 * is the first call, or if the configuration has changed, (re-)prepare 490 * the global data used by NSS. 491 */ 492 static int 493 _nsconfigure(void) 494 { 495 #ifdef _REENTRANT 496 static mutex_t _nsconflock = MUTEX_INITIALIZER; 497 #endif 498 static time_t _nsconfmod; 499 struct stat statbuf; 500 501 mutex_lock(&_nsconflock); 502 503 if (stat(_PATH_NS_CONF, &statbuf) == -1) { 504 /* 505 * No nsswitch.conf; just use whatever configuration we 506 * currently have, or fall back on the defaults specified 507 * by the caller. 508 */ 509 mutex_unlock(&_nsconflock); 510 return (0); 511 } 512 513 if (statbuf.st_mtime <= _nsconfmod) { 514 /* Internal state is up-to-date with nsswitch.conf. */ 515 mutex_unlock(&_nsconflock); 516 return (0); 517 } 518 519 /* 520 * Ok, we've decided we need to update the nsswitch configuration 521 * structures. Acquire a write-lock on _nslock while continuing 522 * to hold _nsconflock. Acquiring a write-lock blocks while 523 * waiting for other threads already holding a read-lock to clear. 524 * We hold _nsconflock for the duration, and update the time stamp 525 * at the end of the update operation, at which time we release 526 * both locks. 527 */ 528 rwlock_wrlock(&_nslock); 529 530 _nsyyin = fopen(_PATH_NS_CONF, "r"); 531 if (_nsyyin == NULL) { 532 /* 533 * Unable to open nsswitch.conf; behave as though the 534 * stat() above failed. Even though we have already 535 * updated _nsconfmod, if the file reappears, the 536 * mtime will change. 537 */ 538 goto out; 539 } 540 541 _NSVECT_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap), 542 (_nsvect_free_elem) _nsdbtfree); 543 _NSVECT_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod), 544 (_nsvect_free_elem) _nsmodfree); 545 546 _nsloadbuiltin(); 547 548 _nsyyparse(); 549 (void) fclose(_nsyyin); 550 if (_nsmapsize != 0) 551 qsort(_nsmap, _nsmapsize, sizeof(*_nsmap), _nsdbtcmp); 552 553 _nsconfmod = statbuf.st_mtime; 554 555 out: 556 rwlock_unlock(&_nslock); 557 mutex_unlock(&_nsconflock); 558 return (0); 559 } 560 561 static nss_method 562 _nsmethod(const char *source, const char *database, const char *method, 563 const ns_dtab disp_tab[], void **cb_data) 564 { 565 int curdisp; 566 ns_mod *mod, modkey; 567 ns_mtab *mtab, mtabkey; 568 569 if (disp_tab != NULL) { 570 for (curdisp = 0; disp_tab[curdisp].src != NULL; curdisp++) { 571 if (strcasecmp(source, disp_tab[curdisp].src) == 0) { 572 *cb_data = disp_tab[curdisp].cb_data; 573 return (disp_tab[curdisp].callback); 574 } 575 } 576 } 577 578 modkey.name = source; 579 mod = bsearch(&modkey, _nsmod, _nsmodsize, sizeof(*_nsmod), 580 _nsmodcmp); 581 if (mod != NULL && mod->handle != NULL) { 582 mtabkey.database = database; 583 mtabkey.name = method; 584 mtab = bsearch(&mtabkey, mod->mtab, mod->mtabsize, 585 sizeof(mod->mtab[0]), _nsmtabcmp); 586 if (mtab != NULL) { 587 *cb_data = mtab->mdata; 588 return (mtab->method); 589 } 590 } 591 592 *cb_data = NULL; 593 return (NULL); 594 } 595 596 int 597 /*ARGSUSED*/ 598 nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, 599 const char *method, const ns_src defaults[], ...) 600 { 601 static int _nsdispatching; 602 #ifdef _REENTRANT 603 struct _ns_drec drec, *ldrec; 604 #endif 605 va_list ap; 606 int i, result; 607 ns_dbt key; 608 const ns_dbt *dbt; 609 const ns_src *srclist; 610 int srclistsize; 611 nss_method cb; 612 void *cb_data; 613 614 /* retval may be NULL */ 615 /* disp_tab may be NULL */ 616 _DIAGASSERT(database != NULL); 617 _DIAGASSERT(method != NULL); 618 _DIAGASSERT(defaults != NULL); 619 if (database == NULL || method == NULL || defaults == NULL) 620 return (NS_UNAVAIL); 621 622 /* 623 * In both the threaded and non-threaded cases, avoid reloading 624 * the configuration if the current thread is already running 625 * nsdispatch() (i.e. recursive call). 626 * 627 * In the non-threaded case, this avoids changing the data structures 628 * while we're using them. 629 * 630 * In the threaded case, this avoids trying to take a write lock 631 * while the current thread holds a read lock (which would result 632 * in deadlock). 633 */ 634 #ifdef _REENTRANT 635 if (__isthreaded) { 636 drec.thr = thr_self(); 637 mutex_lock(&_ns_drec_lock); 638 LIST_FOREACH(ldrec, &_ns_drec, list) { 639 if (ldrec->thr == drec.thr) 640 break; 641 } 642 LIST_INSERT_HEAD(&_ns_drec, &drec, list); 643 mutex_unlock(&_ns_drec_lock); 644 if (ldrec == NULL && _nsconfigure()) { 645 mutex_lock(&_ns_drec_lock); 646 LIST_REMOVE(&drec, list); 647 mutex_unlock(&_ns_drec_lock); 648 return (NS_UNAVAIL); 649 } 650 } else 651 #endif /* _REENTRANT */ 652 if (_nsdispatching++ == 0 && _nsconfigure()) { 653 _nsdispatching--; 654 return (NS_UNAVAIL); 655 } 656 657 rwlock_rdlock(&_nslock); 658 659 key.name = database; 660 dbt = bsearch(&key, _nsmap, _nsmapsize, sizeof(*_nsmap), _nsdbtcmp); 661 if (dbt != NULL) { 662 srclist = dbt->srclist; 663 srclistsize = dbt->srclistsize; 664 } else { 665 srclist = defaults; 666 srclistsize = 0; 667 while (srclist[srclistsize].name != NULL) 668 srclistsize++; 669 } 670 result = 0; 671 672 for (i = 0; i < srclistsize; i++) { 673 cb = _nsmethod(srclist[i].name, database, method, 674 disp_tab, &cb_data); 675 result = 0; 676 if (cb != NULL) { 677 va_start(ap, defaults); 678 result = (*cb)(retval, cb_data, ap); 679 va_end(ap); 680 if (defaults[0].flags & NS_FORCEALL) 681 continue; 682 if (result & srclist[i].flags) 683 break; 684 } 685 } 686 result &= NS_STATUSMASK; /* clear private flags in result */ 687 688 rwlock_unlock(&_nslock); 689 690 #ifdef _REENTRANT 691 if (__isthreaded) { 692 mutex_lock(&_ns_drec_lock); 693 LIST_REMOVE(&drec, list); 694 mutex_unlock(&_ns_drec_lock); 695 } else 696 #endif /* _REENTRANT */ 697 _nsdispatching--; 698 699 return (result ? result : NS_NOTFOUND); 700 } 701