1 /* $NetBSD: nsdispatch.c,v 1.28 2004/11/10 07:23:32 lukem 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.28 2004/11/10 07:23:32 lukem 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 /*LINTED const cast*/ 285 free((void *)mod->name); 286 if (mod->handle == NULL) 287 return; 288 if (mod->unregister != NULL) 289 (*mod->unregister)(mod->mtab, mod->mtabsize); 290 #ifdef __ELF__ 291 if (mod->handle != _nsbuiltin) 292 (void) dlclose(mod->handle); 293 #endif /* __ELF__ */ 294 } 295 296 /* 297 * Load a built-in or dyanamically linked module. If the `reg_fn' 298 * argument is non-NULL, assume a built-in module and use `reg_fn' 299 * to register it. Otherwise, search for a dynamic nsswitch module. 300 */ 301 static int 302 _nsloadmod(const char *source, nss_module_register_fn reg_fn) 303 { 304 #ifdef __ELF__ 305 char buf[PATH_MAX]; 306 #endif 307 ns_mod mod, *new; 308 309 memset(&mod, 0, sizeof(mod)); 310 mod.name = strdup(source); 311 if (mod.name == NULL) 312 return (-1); 313 314 if (reg_fn != NULL) { 315 /* 316 * The placeholder is required, as a NULL handle 317 * represents an invalid module. 318 */ 319 mod.handle = _nsbuiltin; 320 } else if (!is_dynamic()) { 321 goto out; 322 } else { 323 #ifdef __ELF__ 324 if (snprintf(buf, sizeof(buf), "nss_%s.so.%d", mod.name, 325 NSS_MODULE_INTERFACE_VERSION) >= (int)sizeof(buf)) 326 goto out; 327 mod.handle = dlopen(buf, RTLD_LOCAL | RTLD_LAZY); 328 if (mod.handle == NULL) { 329 #ifdef _NSS_DEBUG 330 /* 331 * This gets pretty annoying, since the built-in 332 * sources are not yet modules. 333 */ 334 /* XXX log some error? */ 335 #endif 336 goto out; 337 } 338 reg_fn = (nss_module_register_fn) dlsym(mod.handle, 339 "nss_module_register"); 340 if (reg_fn == NULL) { 341 (void) dlclose(mod.handle); 342 mod.handle = NULL; 343 /* XXX log some error? */ 344 goto out; 345 } 346 #else /* ! __ELF__ */ 347 mod.handle = NULL; 348 #endif /* __ELF__ */ 349 } 350 mod.mtab = (*reg_fn)(mod.name, &mod.mtabsize, &mod.unregister); 351 if (mod.mtab == NULL || mod.mtabsize == 0) { 352 #ifdef __ELF__ 353 if (mod.handle != _nsbuiltin) 354 (void) dlclose(mod.handle); 355 #endif /* __ELF__ */ 356 mod.handle = NULL; 357 /* XXX log some error? */ 358 goto out; 359 } 360 if (mod.mtabsize > 1) 361 qsort(mod.mtab, mod.mtabsize, sizeof(mod.mtab[0]), 362 _nsmtabcmp); 363 out: 364 new = _nsvect_append(&mod, _nsmod, &_nsmodsize, sizeof(*_nsmod)); 365 if (new == NULL) { 366 _nsmodfree(&mod); 367 return (-1); 368 } 369 _nsmod = new; 370 /* _nsmodsize already incremented */ 371 372 qsort(_nsmod, _nsmodsize, sizeof(*_nsmod), _nsmodcmp); 373 return (0); 374 } 375 376 static void 377 _nsloadbuiltin(void) 378 { 379 380 /* Do nothing, for now. */ 381 } 382 383 int 384 _nsdbtaddsrc(ns_dbt *dbt, const ns_src *src) 385 { 386 void *new; 387 const ns_mod *mod; 388 ns_mod modkey; 389 390 _DIAGASSERT(dbt != NULL); 391 _DIAGASSERT(src != NULL); 392 393 new = _nsvect_append(src, dbt->srclist, &dbt->srclistsize, 394 sizeof(*src)); 395 if (new == NULL) 396 return (-1); 397 dbt->srclist = new; 398 /* dbt->srclistsize already incremented */ 399 400 modkey.name = src->name; 401 mod = bsearch(&modkey, _nsmod, _nsmodsize, sizeof(*_nsmod), 402 _nsmodcmp); 403 if (mod == NULL) 404 return (_nsloadmod(src->name, NULL)); 405 406 return (0); 407 } 408 409 void 410 _nsdbtdump(const ns_dbt *dbt) 411 { 412 int i; 413 414 _DIAGASSERT(dbt != NULL); 415 416 printf("%s (%d source%s):", dbt->name, dbt->srclistsize, 417 dbt->srclistsize == 1 ? "" : "s"); 418 for (i = 0; i < dbt->srclistsize; i++) { 419 printf(" %s", dbt->srclist[i].name); 420 if (!(dbt->srclist[i].flags & 421 (NS_UNAVAIL|NS_NOTFOUND|NS_TRYAGAIN)) && 422 (dbt->srclist[i].flags & NS_SUCCESS)) 423 continue; 424 printf(" ["); 425 if (!(dbt->srclist[i].flags & NS_SUCCESS)) 426 printf(" SUCCESS=continue"); 427 if (dbt->srclist[i].flags & NS_UNAVAIL) 428 printf(" UNAVAIL=return"); 429 if (dbt->srclist[i].flags & NS_NOTFOUND) 430 printf(" NOTFOUND=return"); 431 if (dbt->srclist[i].flags & NS_TRYAGAIN) 432 printf(" TRYAGAIN=return"); 433 printf(" ]"); 434 } 435 printf("\n"); 436 } 437 438 static void 439 _nssrclist_free(ns_src **src, u_int srclistsize) 440 { 441 u_int i; 442 443 for (i = 0; i < srclistsize; i++) { 444 if ((*src)[i].name != NULL) { 445 /*LINTED const cast*/ 446 free((void *)(*src)[i].name); 447 } 448 } 449 free(*src); 450 *src = NULL; 451 } 452 453 static void 454 _nsdbtfree(ns_dbt *dbt) 455 { 456 457 _nssrclist_free(&dbt->srclist, dbt->srclistsize); 458 if (dbt->name != NULL) { 459 /*LINTED const cast*/ 460 free((void *)dbt->name); 461 } 462 } 463 464 int 465 _nsdbtput(const ns_dbt *dbt) 466 { 467 ns_dbt *p; 468 void *new; 469 u_int i; 470 471 _DIAGASSERT(dbt != NULL); 472 473 for (i = 0; i < _nsmapsize; i++) { 474 p = _nsvect_elem(i, _nsmap, _nsmapsize, sizeof(*_nsmap)); 475 if (strcasecmp(dbt->name, p->name) == 0) { 476 /* overwrite existing entry */ 477 if (p->srclist != NULL) 478 _nssrclist_free(&p->srclist, p->srclistsize); 479 memmove(p, dbt, sizeof(*dbt)); 480 return (0); 481 } 482 } 483 new = _nsvect_append(dbt, _nsmap, &_nsmapsize, sizeof(*_nsmap)); 484 if (new == NULL) 485 return (-1); 486 _nsmap = new; 487 /* _nsmapsize already incremented */ 488 489 return (0); 490 } 491 492 /* 493 * This function is called each time nsdispatch() is called. If this 494 * is the first call, or if the configuration has changed, (re-)prepare 495 * the global data used by NSS. 496 */ 497 static int 498 _nsconfigure(void) 499 { 500 #ifdef _REENTRANT 501 static mutex_t _nsconflock = MUTEX_INITIALIZER; 502 #endif 503 static time_t _nsconfmod; 504 struct stat statbuf; 505 506 mutex_lock(&_nsconflock); 507 508 if (stat(_PATH_NS_CONF, &statbuf) == -1) { 509 /* 510 * No nsswitch.conf; just use whatever configuration we 511 * currently have, or fall back on the defaults specified 512 * by the caller. 513 */ 514 mutex_unlock(&_nsconflock); 515 return (0); 516 } 517 518 if (statbuf.st_mtime <= _nsconfmod) { 519 /* Internal state is up-to-date with nsswitch.conf. */ 520 mutex_unlock(&_nsconflock); 521 return (0); 522 } 523 524 /* 525 * Ok, we've decided we need to update the nsswitch configuration 526 * structures. Acquire a write-lock on _nslock while continuing 527 * to hold _nsconflock. Acquiring a write-lock blocks while 528 * waiting for other threads already holding a read-lock to clear. 529 * We hold _nsconflock for the duration, and update the time stamp 530 * at the end of the update operation, at which time we release 531 * both locks. 532 */ 533 rwlock_wrlock(&_nslock); 534 535 _nsyyin = fopen(_PATH_NS_CONF, "r"); 536 if (_nsyyin == NULL) { 537 /* 538 * Unable to open nsswitch.conf; behave as though the 539 * stat() above failed. Even though we have already 540 * updated _nsconfmod, if the file reappears, the 541 * mtime will change. 542 */ 543 goto out; 544 } 545 546 _NSVECT_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap), 547 (_nsvect_free_elem) _nsdbtfree); 548 _NSVECT_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod), 549 (_nsvect_free_elem) _nsmodfree); 550 551 _nsloadbuiltin(); 552 553 _nsyyparse(); 554 (void) fclose(_nsyyin); 555 if (_nsmapsize != 0) 556 qsort(_nsmap, _nsmapsize, sizeof(*_nsmap), _nsdbtcmp); 557 558 _nsconfmod = statbuf.st_mtime; 559 560 out: 561 rwlock_unlock(&_nslock); 562 mutex_unlock(&_nsconflock); 563 return (0); 564 } 565 566 static nss_method 567 _nsmethod(const char *source, const char *database, const char *method, 568 const ns_dtab disp_tab[], void **cb_data) 569 { 570 int curdisp; 571 ns_mod *mod, modkey; 572 ns_mtab *mtab, mtabkey; 573 574 if (disp_tab != NULL) { 575 for (curdisp = 0; disp_tab[curdisp].src != NULL; curdisp++) { 576 if (strcasecmp(source, disp_tab[curdisp].src) == 0) { 577 *cb_data = disp_tab[curdisp].cb_data; 578 return (disp_tab[curdisp].callback); 579 } 580 } 581 } 582 583 modkey.name = source; 584 mod = bsearch(&modkey, _nsmod, _nsmodsize, sizeof(*_nsmod), 585 _nsmodcmp); 586 if (mod != NULL && mod->handle != NULL) { 587 mtabkey.database = database; 588 mtabkey.name = method; 589 mtab = bsearch(&mtabkey, mod->mtab, mod->mtabsize, 590 sizeof(mod->mtab[0]), _nsmtabcmp); 591 if (mtab != NULL) { 592 *cb_data = mtab->mdata; 593 return (mtab->method); 594 } 595 } 596 597 *cb_data = NULL; 598 return (NULL); 599 } 600 601 int 602 /*ARGSUSED*/ 603 nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, 604 const char *method, const ns_src defaults[], ...) 605 { 606 static int _nsdispatching; 607 #ifdef _REENTRANT 608 struct _ns_drec drec, *ldrec; 609 #endif 610 va_list ap; 611 int i, result; 612 ns_dbt key; 613 const ns_dbt *dbt; 614 const ns_src *srclist; 615 int srclistsize; 616 nss_method cb; 617 void *cb_data; 618 619 /* retval may be NULL */ 620 /* disp_tab may be NULL */ 621 _DIAGASSERT(database != NULL); 622 _DIAGASSERT(method != NULL); 623 _DIAGASSERT(defaults != NULL); 624 if (database == NULL || method == NULL || defaults == NULL) 625 return (NS_UNAVAIL); 626 627 /* 628 * In both the threaded and non-threaded cases, avoid reloading 629 * the configuration if the current thread is already running 630 * nsdispatch() (i.e. recursive call). 631 * 632 * In the non-threaded case, this avoids changing the data structures 633 * while we're using them. 634 * 635 * In the threaded case, this avoids trying to take a write lock 636 * while the current thread holds a read lock (which would result 637 * in deadlock). 638 */ 639 #ifdef _REENTRANT 640 if (__isthreaded) { 641 drec.thr = thr_self(); 642 mutex_lock(&_ns_drec_lock); 643 LIST_FOREACH(ldrec, &_ns_drec, list) { 644 if (ldrec->thr == drec.thr) 645 break; 646 } 647 LIST_INSERT_HEAD(&_ns_drec, &drec, list); 648 mutex_unlock(&_ns_drec_lock); 649 if (ldrec == NULL && _nsconfigure()) { 650 mutex_lock(&_ns_drec_lock); 651 LIST_REMOVE(&drec, list); 652 mutex_unlock(&_ns_drec_lock); 653 return (NS_UNAVAIL); 654 } 655 } else { 656 if (_nsdispatching == 0 && _nsconfigure()) 657 return (NS_UNAVAIL); 658 _nsdispatching = 1; 659 } 660 #else 661 if (_nsdispatching == 0 && _nsconfigure()) 662 return (NS_UNAVAIL); 663 _nsdispatching = 1; 664 #endif /* _REENTRANT */ 665 666 rwlock_rdlock(&_nslock); 667 668 key.name = database; 669 dbt = bsearch(&key, _nsmap, _nsmapsize, sizeof(*_nsmap), _nsdbtcmp); 670 if (dbt != NULL) { 671 srclist = dbt->srclist; 672 srclistsize = dbt->srclistsize; 673 } else { 674 srclist = defaults; 675 srclistsize = 0; 676 while (srclist[srclistsize].name != NULL) 677 srclistsize++; 678 } 679 result = 0; 680 681 for (i = 0; i < srclistsize; i++) { 682 cb = _nsmethod(srclist[i].name, database, method, 683 disp_tab, &cb_data); 684 result = 0; 685 if (cb != NULL) { 686 va_start(ap, defaults); 687 result = (*cb)(retval, cb_data, ap); 688 va_end(ap); 689 if (defaults[0].flags & NS_FORCEALL) 690 continue; 691 if (result & srclist[i].flags) 692 break; 693 } 694 } 695 result &= NS_STATUSMASK; /* clear private flags in result */ 696 697 rwlock_unlock(&_nslock); 698 699 #ifdef _REENTRANT 700 if (__isthreaded) { 701 mutex_lock(&_ns_drec_lock); 702 LIST_REMOVE(&drec, list); 703 mutex_unlock(&_ns_drec_lock); 704 } else 705 _nsdispatching = 0; 706 #else 707 _nsdispatching = 0; 708 #endif /* _REENTRANT */ 709 710 return (result ? result : NS_NOTFOUND); 711 } 712